Velocity variables in multiple scripts causing confusion | Community
Skip to main content
Level 2
June 23, 2025
Solved

Velocity variables in multiple scripts causing confusion

  • June 23, 2025
  • 1 reply
  • 1278 views

Hi Everyone, 

 

I seemed to have unknowingly stumbled upon Velocity's ability to declare and reference variables across multiple tokens. After removing one debug token from an email, subsequent script tokens stop evaluating and fall back to their default values. Re-adding the debug token “fixes” the issue, but I don’t fully understand why.

 

Context

  • I’m working on an auto-renewal email that pulls fields from a related Opportunity with Velocity in a few tokens (Pre-AR notification date, plus 30-/90-day offsets).

  • I had a temporary “debug” Velocity token in the template that printed out all calculated dates and metadata. That worked perfectly.

  • Once I removed that debug token (to clean up before final approval), the main script token suddenly rendered its fallback (“no date found”) for every recipient—even though the underlying Opportunity data was unchanged.

  • If I re-insert the debug token back into the email, everything renders correctly again.

  • In the attached image Token 1 is the debugging token, and Tokens 2 and 3 are the production ones.

 

Issue Statement

When multiple Velocity script tokens exist in the same email asset, Marketo appears to stop processing subsequent tokens once the first one is removed—even if those other tokens haven’t changed. I’m looking for insight into how Marketo’s Velocity engine declares or scopes its variables and why the presence (or absence) of an earlier script token affects later ones. Has anyone seen this behavior, and can you explain what’s happening under the hood or suggest a better workaround?

 

Token Scripts:

Token 1 (Debugging):

 

## --- Set required Velocity variables for timezone and formatting --- #set( $defaultTimeZone = $date.getTimeZone().getTimeZone("America/New_York") ) #set( $defaultLocale = $date.getLocale() ) #set( $calNow = $date.getCalendar() ) #set( $ret = $calNow.setTimeZone($defaultTimeZone) ) #set( $calConst = $field.in($calNow) ) #set( $ISO8601DateOnly = "yyyy-MM-dd" ) ## --- Format today's date as yyyy-MM-dd string --- #set( $today = $date.format($ISO8601DateOnly, $calNow, $defaultLocale, $defaultTimeZone) ) ## --- Find first matching opportunity --- #set($matchingOpp = "") #foreach($opp in $OpportunityList) #if( $opp.Pre_Auto_Renewal_Notification_Date__c && $opp.Stage != "Closed Lost" && !$opp.Auto_Renewal_Opt_Out__c && $opp.Type == "Auto Renewal - Pending" ) #set($matchingOpp = $opp) #break #end #end ## --- Output calculated date values --- #if($matchingOpp != "" && $matchingOpp.Pre_Auto_Renewal_Notification_Date__c) ## Set Opp Name #set($oppName = $matchingOpp.Name) ## Parse string to Date #set($baseDate = $convert.parseDate($matchingOpp.Pre_Auto_Renewal_Notification_Date__c, $ISO8601DateOnly, $defaultLocale, $defaultTimeZone)) ## Convert to Calendar and add days #set($cal30 = $convert.toCalendar($baseDate)) #set($cal90 = $convert.toCalendar($baseDate)) $cal30.add($calConst.DATE, 30) $cal90.add($calConst.DATE, 90) ## Format and print output Opp Name: $oppName<br> Pre AR Date: $date.format($ISO8601DateOnly, $baseDate, $defaultLocale, $defaultTimeZone)<br/> +30 Days: $date.format($ISO8601DateOnly, $cal30, $defaultLocale, $defaultTimeZone)<br/> +90 Days: $date.format($ISO8601DateOnly, $cal90, $defaultLocale, $defaultTimeZone) #else no date found #end

Token 2 (30 day offset) 

## --- Set required Velocity variables for timezone and formatting --- #set( $defaultTimeZone = $date.getTimeZone().getTimeZone("America/New_York") ) #set( $defaultLocale = $date.getLocale() ) #set( $calNow = $date.getCalendar() ) #set( $ret = $calNow.setTimeZone($defaultTimeZone) ) #set( $calConst = $field.in($calNow) ) #set( $ISO8601DateOnly = "MM/dd/yyyy" ) ## --- Find first matching opportunity --- #set($matchingOpp = "") #foreach($opp in $OpportunityList) #if( $opp.Pre_Auto_Renewal_Notification_Date__c && $opp.Stage != "Closed Lost" && !$opp.Auto_Renewal_Opt_Out__c && $opp.Type == "Auto Renewal - Pending" ) #set($matchingOpp = $opp) #break #end #end ## --- Output calculated date values --- #if($matchingOpp != "" && $matchingOpp.Pre_Auto_Renewal_Notification_Date__c) ## Parse string to Date #set($baseDate = $convert.parseDate($matchingOpp.Pre_Auto_Renewal_Notification_Date__c, $ISO8601DateOnly, $defaultLocale, $defaultTimeZone)) ## Convert to Calendar and add days #set($cal30 = $convert.toCalendar($baseDate)) #set($cal90 = $convert.toCalendar($baseDate)) $cal30.add($calConst.DATE, 30) $cal90.add($calConst.DATE, 90) ## Format and print output $date.format($ISO8601DateOnly, $cal30, $defaultLocale, $defaultTimeZone)#else no date found#end

 Token 3 (90 Offset):

## --- Set required Velocity variables for timezone and formatting --- #set( $defaultTimeZone = $date.getTimeZone().getTimeZone("America/New_York") ) #set( $defaultLocale = $date.getLocale() ) #set( $calNow = $date.getCalendar() ) #set( $ret = $calNow.setTimeZone($defaultTimeZone) ) #set( $calConst = $field.in($calNow) ) #set( $ISO8601DateOnly = "MM/dd/yyyy" ) ## --- Find first matching opportunity --- #set($matchingOpp = "") #foreach($opp in $OpportunityList) #if( $opp.Pre_Auto_Renewal_Notification_Date__c && $opp.Stage != "Closed Lost" && !$opp.Auto_Renewal_Opt_Out__c && $opp.Type == "Auto Renewal - Pending" ) #set($matchingOpp = $opp) #break #end #end ## --- Output calculated date values --- #if($matchingOpp != "" && $matchingOpp.Pre_Auto_Renewal_Notification_Date__c) ## Parse string to Date #set($baseDate = $convert.parseDate($matchingOpp.Pre_Auto_Renewal_Notification_Date__c, $ISO8601DateOnly, $defaultLocale, $defaultTimeZone)) ## Convert to Calendar and add days #set($cal30 = $convert.toCalendar($baseDate)) #set($cal90 = $convert.toCalendar($baseDate)) $cal30.add($calConst.DATE, 30) $cal90.add($calConst.DATE, 90) ## Format and print output $date.format($ISO8601DateOnly, $cal90, $defaultLocale, $defaultTimeZone)#else no date found#end
This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by SanfordWhiteman

Thank you, Sandy!

 

Is there a way to format the date to MM/DD/YYYY?


Of course!

#set( $CustomDateFormat = "MM/dd/yyyy" )

 

That’s what you pass to $date.format(), not to $convert.parseDate().

1 reply

SanfordWhiteman
Level 10
June 23, 2025

I seemed to have unknowingly stumbled upon Velocity's ability to declare and reference variables across multiple tokens.

Of course. A Marketo email is one big Velocity template!

 

All the VTL components share a single scope, just as they would if placed end-to-end in a single {{my.token}}. Splitting across multiple tokens is highly recommended because it allows modular development. Don’t know where I’d be without the ability to use multiple tokens — some inherited, some local.

 


Issue Statement

When multiple Velocity script tokens exist in the same email asset, Marketo appears to stop processing subsequent tokens once the first one is removed


No, that’s not true.

 

Most likely cause is you have certain fields checked off in the tree in Script Editor in Token 1, but you didn’t check them in Token 2/3. (This a vital feature as only one token needs to have the fields checked. But you need to include that one!)

 

 

 

 

Level 2
June 23, 2025

Hi Sanford, 

 

Thank you so much for the quick reply! That was totally it - the initial debugging token had all of the field checked off in the template but the production tokens didn't. 

 

However, that brings me to my next question. I'm now seeing the following values that I didn't see before.

 

$cal30.add($calConst.DATE, 30) $cal90.add($calConst.DATE, 90) $date.format($ISO8601DateOnly, $cal30, $defaultLocale, $defaultTimeZone)

 

SanfordWhiteman
Level 10
June 24, 2025

When you see raw Velocity code, that’s because there was an error calling a method (this is how Velocity avoids throwing fatal errors all the time, although it’s still possible to have fatal errors in lots of cases).

 

To help further I’d need to see the raw output of

#foreach( $oppty in $OpportunityList) #foreach( $kv in $oppty.entrySet() ) $kv.getValue().class $kv.getKey() $kv.getValue() #end #end