Velocity variables in multiple scripts causing confusion
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
#endToken 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#endToken 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