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! Because on line 1 the (overly naïve) email parser does a split() on @ and on line 2 assumes the resulting array has at least 2 items.

 

This assumption is true of any valid email address. But when you approve, the placeholder email address in $lead.Email, as for all Person fields, is an empty string. An empty string can be split() without an error. But the array only has 1 item (also an empty string!). So if you try to seek the 2nd item — index [1] as 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 ensuring $lead.Email isn’t empty: this will let you approve the asset. If somebody’s email is valid, their address must have an @ sign, so you don’t necessarily need to check for that:

#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

 

If you want to go beyond approval and also cover people with an invalid — but non-empty – $lead.Email, switch the logic so you always split() but check the 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.