Help! My Velocity {{my.token}} previews correctly, but I canโ€™t approve the email

Velocity is already verbose, but youโ€™ve still gotta code defensively even if that means a little more code.

Crucially, in the Marketo-Velocity ecosystem, donโ€™t assume code will only run in the context of a Person record. Prepare for person fields and object lists to be empty. This is true, for example, during asset approval.

Take this little snippet that adds a footer including the email domain:

#set( $emailParts = $lead.Email.split("@") )
#set( $emailDomain = $emailParts[1] )
If this email was mistakenly routed to your spam folder, ask the administrator of the ${emailDomain} mailserver to allow future emails from info@example.com. 

Itโ€™ll run fine when you preview by a Person or List, but when you try to approve youโ€™ll get:

You canโ€™t see the full error in the popup, but if you look at the underlying Java error itโ€™s an ArrayIndexOutOfBoundsException.[1]

And this makes sense! On line 1 the (overly naรฏve) email parser does a split() on @, then on line 2 it assumes the resulting array has at least 2 items.

This assumption is true of any valid email address. But when you approve, the placeholder value in $lead.Email, as with all Person fields checked off in Script Editor, is an empty string. You can split() an empty string, no problem. But the array only has 1 item (also an empty string). So if you try to seek the 2nd item โ€” index [1] since arrays are 0-based โ€” thatโ€™s out of bounds.

While Velocity swallows a lot of errors that would be fatal in pure Java, this isnโ€™t one of them. Exceeding the length of an array is always fatal.

Coding for Approve Email

Simplest defensive move is checking if $lead.Email is empty before continuing. This will let you approve the asset:

#if( !$lead.Email.isEmpty() )
#set( $emailParts = $lead.Email.split("@") )
If this email was mistakenly routed to your spam folder, ask the administrator of the ${emailDomain} mailserver to allow future emails from info@example.com.
#end

The above assumes $lead.email is either empty or valid. A valid address must have an @ sign, so it doesnโ€™t check beyond that.

If you want to also cover people with an invalid โ€” but non-empty โ€“ $lead.Email, switch it around so you split() and check the expected length:

#set( $emailParts = $lead.Email.split("@") )
#if( $emailParts.size() > 1 )
#set( $emailDomain = $emailParts[1] )
If this email was mistakenly routed to your spam folder, ask the administrator of the ${emailDomain} mailserver to allow future emails from info@example.com.
#end

Even better, rethink split-and-get-2nd-item. As noted above, this is a naรฏve way of parsing the domain out of an email address.[2] Donโ€™t you really mean split-and-get-last-item? I think you do. So if you do that, no matter whether $lead.email is empty, valid, or invalid, thereโ€™s no error:

#set( $emailParts = $lead.Email.split("@") )
#set( $emailDomain = $emailParts[$math.sub($emailParts.size(),1)] )
If this email was mistakenly routed to your spam folder, ask the administrator of the ${emailDomain} mailserver to allow future emails from info@example.com. 
Notes

[1] In a local Velocity environment you see the stack trace:

Error invoking method 'get(java.lang.Integer)' in [Ljava.lang.String; at dynmacro.vm[line 52, column 33]
        at org.apache.velocity.runtime.parser.node.ASTIndex.execute(ASTIndex.java:175)
        at org.apache.velocity.runtime.parser.node.ASTReference.execute(ASTReference.java:280)
        at org.apache.velocity.runtime.parser.node.ASTReference.value(ASTReference.java:567)
        at org.apache.velocity.runtime.parser.node.ASTExpression.value(ASTExpression.java:71)
        at org.apache.velocity.runtime.parser.node.ASTSetDirective.render(ASTSetDirective.java:142)
        at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:342)
        at org.apache.velocity.Template.merge(Template.java:356)
        at org.apache.velocity.Template.merge(Template.java:260)
        at VTLRunner.main(VTLRunner.java:92)
Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.doInvoke(UberspectImpl.java:395)
        at org.apache.velocity.util.introspection.UberspectImpl$VelMethodImpl.invoke(UberspectImpl.java:384)
        at org.apache.velocity.runtime.parser.node.ASTIndex.execute(ASTIndex.java:149)
        ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException
        at java.lang.reflect.Array.get(Native Method)
        at org.apache.velocity.util.ArrayListWrapper.get(ArrayListWrapper.java:43)

[2] Itโ€™s wrong for old-school but valid syntax like "sandy@home"@example.com.