To catch, or not to catch, that is debugging.
Table of Contents
When you start working with programmatic languages in Salesforce Marketing Cloud, you will quickly become close friends with the 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?
Before diving deep into errors, let's talk about 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 the 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.
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|
The first type of error you might encounter is the dreaded 500 Error 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.
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 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 the
- 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 a much more basic and less helpful but built-in solution to 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.
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 handy 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 a
response variable within my
Write. The string before describes what is printed (useful mainly if you use multiple Writes in your code). The one after -
<br><br> - will separate 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
- 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 create a script for the long-term, it is good 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
You Should Know
Make your life easier with a nice clean function that will save you a lot of time in the long run:
With this approach, once you copy-paste the function, you have cleaner debugging lines, less code to write, no worry whether you need to
Stringify or not and automatic new line addition for easier reading. Just pass debug description and the value you want to check and see the magic happen.
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)
Alternatively use ternary to cover all scenarios:
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.
As you can see, this response is quite long. The most important information is in the last sentence of first paragraph. In this example we learn that
System.Net.WebException: The remote name could not be resolved: 'www.example.c' - from System. It makes it easy to understand that there is issue with the URL we provided.
You can also see why Try/Catch block is so valuable, as in second paragraph our
undefined, which isn't very helpful. This is due to the fact, that because of the invalid URL, the GET did not return to response variable.
Try/Catch is especially useful for SSJS Functions that call out-of-page data. Marketing Cloud Data Extensions, Salesforce Objects or even data sources from outside the Salesforce environment. It also will help when you are handling responses from such sources. For example, trying to get value from a nested object.
In most cases, the simple Try/Catch will be all you need, but there is also a third element that might be very helpful:
finally. Its purpose is to store a code that will execute, no matter what. Great for a cleanup code that must run regardless whether the previous logic was successful or not.
Out of the three, SSJS requires only Try. You can use it with Catch, Finally or both, depending on your use case.
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 modify their values within the block:
You can leverage the Try/Catch block further by implementing your custom errors with
throw. Why would you break your own code on purpose? It will help you control the execution of the code - especially if it uses external data. It can also be great for debugging and streamlining user experience.
In the above example, you can see that we are checking whether the response from our reCaptcha validation to know whether we reached the Google server (else we throw
reCAPTCHA API error) and if yes - was the Captcha solved.
Here I'm using the string passed to
throw just for debugging purposes, but you can use it for an overlay with feedback to your client or log the error.
You Should Know
You are not limited to strings with the
throw. You can also pass objects to mimic the Marketing Cloud errors. It will make error handling much easier:
Once you debug your code using the above methods, it is still good to program defensively and leverage the Try/Catch block in the production environment. It will allow you to handle the errors for the customers and 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 creating a JIRA bug ticket.
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 any new issues.
Here you can find a 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 a retention period. Align it with the internal process for checking it for new issues. Seven days per record is a good starting point.
Instead of writing Try/Catch blocks for every potentially risky function, you might create just one block that will capture the 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 more comfortable to read but limit your control over specific handling for various scenarios.
You Should Know
Any redirect within your
try block is recognized as an error and caught. If you need to use a redirect, don't use the 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 instrumental when you want to leverage redirect to handle the customer's error.
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 a single Error Handler function and just calling it wherever it is needed:
Whenever the 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 the 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 🔗
- Use the
Writefunction to understand what happens with your variables during execution 🔗
- Use the 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 🔗