To catch, or not to catch, that is debugging.
When you start working with programmatic languages in Salesforce Marketing Cloud, you will quickly become close friends with Error 500 page. And if you beat it and go forward with the code and official documentation, you will fall in love with unexpected errors in functions and API responses. How to survive those?
SSJS Testing Ground
Before diving deep into errors, let's talk where to write the SSJS. The language is useful in many places within the Marketing Cloud ecosystem. Among else in:
- Cloud Pages (Web Studio)
- Code Resources (Web Studio)
- Content Blocks (Content Builder)
- Emails and other communication (Email Studio / Mobile Studio / Content Builder)
- Script Activities (Automation Studio)
In all of the above cases, you might write more complex logic that will be error-prone and in need of debugging. However, only a few of those are really good to test your code, as only some of them provide access to methods mentioned later in the article.
It has, however, two flaws that, in some scenarios, might guide you to a different solution.
- Cost. Each view of the Cloud Page costs one Super Message. During debugging, you might hit quite a lot of those. And those tests - across your team and over time - stack up.
- Speed and context. Each time you want to republish updated Cloud Page you have to go through the Preview window (that load the whole code in POST method context), confirm it and go to the page via URL to see the GET method context.
JSON Code Resource
When I want to go around those issues, I work in JSON Code Resource (also available in Web Studio). It generates a link you can use to check whether everything works, just like a Cloud Page. However, page views are free. There is also no preview allowing for a faster save-reload cycle.
|If you need||Cloud Page||JSON Code Resource|
Some also use Email Studio for testing various scripts, but I recommend against it. It is slower than Code Resource and doesn't support SSJS Core Library. And even if you don't need it for your script, it is handy for debugging.
500 - Internal Server Error
The first type of error you might encounter is the dreaded 500 Error that you see right after trying to run your code. Reason? The backend code (SSJS/AMPScript) is invalid. There might be a few reasons for this. The most popular are:
- Typo in SSJS Function Name
- Unclosed or wrongly closed Bracket
- Lack of declaration (in most cases either missing variable declaration,
var soap = new Script.Util.WSProxy();while using it later in the code)
How to deal with those issues? Apart from just reading through your code line by line, there are two faster solutions.
Divide and Conquer
The first one can be done directly in the Marketing Cloud but is a bit of a brute-force approach. You block half of your code from running by enclosing it in a multiline comment (
/* here goes the code */) and check again. If it works, you have half of the SSJS validated (from the 500 error perspective). If not, you split the remaining code in half and comment it out. Rinse, repeat. This way, you can quite quickly find the few lines that are the source of the issue and focus on validating them.
For many issues leading to 500 error, there is an even faster solution, but it requires some pre-work:
- You need to have Visual Studio Code with ESLint extension installed.
- You need to have NodeJS installed.
- You need to have a folder where you will store your SSJS code in files with
- You need to install the excellent ESLing configuration for SSJS made by Joern Berkefeld. To do this, run in terminal
npm i -D eslint eslint-config-ssjsfor the SSJS folder.
Once done, you have automatic SSJS code validation (again, only from the perspective of 500 error - but it is still worth it).
There is also much more basic and less helpful, but built-in solution that can validate your SSJS code directly in the Marketing Cloud. Script Activity in Automation Studio. After adding your code there and clicking Validate Syntax button, it will check it against some basic rules. However, I recommend going with the ESLint. It provides much more value and is faster once configured.
What about Try/Catch?
There will be a separate section about Try/Catch. It does not work for issues leading to 500 Error.
Write the Error down
You got past the 500 error. Great! The script is still not outputting what you wanted? It is time to check what else might be wrong. The easiest way is to use the
Write function. It shows whatever you pass in it to the frontend view. It is especially useful as SSJS does not support console.log due to its server-side nature (a little trick to make it work is below).
To use this function just add
Write('This will be visible on the website') between
<script runat="server"> Platform.Load('Core','1'); and
Of course, in real life, you will probably want to use it for variables:
As you can see in the code snippet above I have more than just
response variable within my
Write. The string before describes what is printed (useful especially if you use multiple Writes in your code). The one after -
<br><br> - will separete it from the rest of the website content (however, as it is HTML, it won't work in JSON Code Resource. You can use
\n\n instead for the same outcome). I highly recommend this approach.
Few things to remember
Writeis Core Library function, so you need to load it first in your script with
- As all Core Library functions, it will not function in Emails or SMS.
- If the variable you want to output is an object, you need to parse it to a string using
- If you don't want to load Core Library use Platform versions of those functions:
Whenever you are creating a script for a long-term, it is a good idea to keep your debugging
Write functions. There might be new requirements. Data sources change. Marketing Cloud too. And sometimes those things might break your code.
Of course, keeping the
Writes as above is a no-go. You don't want your customers to see those. But there is a neat little trick to eat the cake and have it too:
I added two things:
- There is
var debugging = true;near the top. When debugging, keep it on
true. When you publish it for production - change the value to
Writeis within the
ifblock. It now works only if the debugging variable equals
I mentioned before that SSJS does not support
Passing Objects from SSJS to JS
- In SSJS create
var parsedResponse = Stringify(response);
- In JS change the personalization to
console.log(<ctrl:var name=parsedResponse />)(notice different
namevalue and lack of quotes around personalization string)
Try to Catch the Error
Write is a great and simple solution, but it will work only if the code runs correctly. And sometimes it won't. For those cases, add a Try/Catch block. Check the Response tab below to compare the difference between standard
Write(response) and writing the caught error.
Try/Catch is especially useful for SSJS Functions that are calling out-of-page data. Marketing Cloud Data Extensions, Salesforce Objects or even data sources from outside Salesforce environment. It also will help when you are handling responses from such sources. For example, trying to get value from a nested object.
You should know
Try/Catch block in SSJS has separate scope. It means that any variables declared within the block won't be accessible from outside. Neither in the other parts of SSJS nor by personalization strings.
There is a way to overcome this limitation. Declare the variables you want to use globally before the
try and just modify their values within the block:
Once you debug your code using the above methods, it is still a good idea to program defensively and leverage the Try/Catch block even on the production environment. It will allow you to handle the errors for the customers and to log the errors to a data extension (it will provide you with a history of problems along with a description of what happened). You can even build something more sophisticated like automatically create a bug ticket in JIRA.
Logging to Data Extension
The easiest way to log your errors is to leverage Marketing Cloud Data Extensions.
My recommendation is to create one Data Extension that will capture all the errors from the whole instance. It will make it much easier and faster to check whether there are any new issues.
Here you can find sample setup:
|Column Name||Primary Key||Length||Type||Nullable||Default Value|
If you have multiple Business Units on your instance, add it as a Shared Data Extension.
It shouldn't be sendable or testable, but consider adding retention period. Align it with the internal process for checking it for new issues. Seven days per record is a good starting point.
Global Error Catching
Instead of writing Try/Catch blocks for every potentially risky function, you might create just one block that will capture whole code. You can even use it to catch errors in your AMPScript, as described by Zuzanna Jarczyńska.
This approach might make your code shorter and easier to read, but will limit your control over specific handling for various scenarios.
You Should Know
Any kind of redirect within your
try block is recognized as an error and caught. If you need to use a redirect, don't use global Try/Catch approach and instead leverage it only for potentially problematic code.
You can also leverage one of the approaches mentioned by Gortonington in his article on Try/Catch. It might be especially useful, when you want to leverage redirect as a form of handling the error for the customer.
Error Handler Function
Once you start implementing the solutions mentioned above, it might get quite repetitive to add the same lines for conditional
Write, log to Data Extension, et cetera.
You can solve it by creating signle Error Handler function and just calling it wherever it is needed:
Whenever Try/Catch block finds an error, the above snippet will check the value of debugging variable. If it is equal to
true, it will just Write the error to the page to make it easier to debug quickly. If it is
false, it will add the error to the Data Extension instead.
Most of the Marketing Cloud Errors are objects with two keys:
description. Putting those elements into separate columns of the Data Extension make it easier to read the log.
Using Error Handler Function is useful especially when you are triggering it multiple times or when you have elaborate logic for handling (for example API calls to external systems for bug tracking).
You Should Know
You can also create your custom error, by passing a custom object to the
If you have any problems with your SSJS code:
- Test it in Cloud Page or JSON Code Resource 🔗
- Overcome 500 Error with linting or divide & conquer methodology 🔗
Writefunction to understand what happens with your variables during execution 🔗
- Use Try/Catch block to understand better what is the reason for errors you encounter 🔗
- Save errors from your production code to easily track and solve the problems 🔗
- Simplify the approach with custom Error Handler Function 🔗