Error Handling
try/catch/finally, throw, Error objects, and error handling patterns for robust SFMC SSJS.
try / catch / finally
try {
var result = riskyOperation();
Write("Success: " + result);
} catch (e) {
Write("Error: " + e.message);
} finally {
// Runs always — cleanup code
Write("Done.");
}
All three blocks can be used independently:
try { /* ... */ }
catch (e) { /* ... */ } // catch only
try { /* ... */ }
finally { /* ... */ } // no catch — errors still propagate, finally still runs
try { /* ... */ }
catch (e) { /* ... */ }
finally { /* ... */ } // full pattern
Show test script
<script runat="server">
/*
* Chapter: try/catch/finally
* Proves:
* 1. finally runs; call-form message.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var saw = false, msg = "";
try { throw Error("Something went wrong"); } catch (e) { msg = "" + e.message; } finally { saw = true; }
assert("msg", msg, "Something went wrong"); assert("finally", saw ? "true" : "false", "true");
</script>
The Error Object
When an exception is caught, e is an error-like object with a message property:
try {
throw new Error("Something went wrong");
} catch (e) {
Write(e.message); // "Something went wrong"
Write(Platform.Function.Stringify(e)); // full object as JSON
}
Note: The structure of the caught object depends on what was thrown. SSJS platform errors may not always conform to the standard Error shape.
Show test script
<script runat="server">
/*
* Chapter: Error Object
* Proves:
* 1. DEV new Error message unset; call-form sets it.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var neo = new Error("boom");
assert("DEV new msg unset", neo.message === undefined ? "undefined" : "other", "undefined");
assert("String recovers", String(neo).indexOf("boom") >= 0 ? "true" : "false", "true");
var call = Error("call-boom");
assert("call msg", "" + call.message, "call-boom");
</script>
throw
Throw any value — typically a new Error(message):
function getSubscriber(sk) {
if (!sk) {
throw new Error("SubscriberKey is required");
}
// String() first — a Lookup result throws on a truthiness test when the field is empty
var email = String(Platform.Function.Lookup("Subscribers", "Email", "SubscriberKey", sk));
if (email === "" || email === "null") {
throw new Error("Subscriber not found: " + sk);
}
return email;
}
try {
var email = getSubscriber(subscriberKey);
Write(email);
} catch (e) {
Platform.Response.Redirect("/error?msg=" + Platform.Function.UrlEncode(e.message), false);
}
Show test script
<script runat="server">
/*
* Chapter: throw
* Proves:
* 1. throw Error catchable.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var msg = ""; try { throw Error("x"); } catch (e) { msg = "" + e.message; }
assert("thrown", msg, "x");
</script>
Common Error Patterns
Global Try/Catch Wrapper
Wrap your entire CloudPage in a top-level try/catch to prevent blank white pages:
<script runat="server">
Platform.Load("core", "1.1.5");
try {
// All page logic here
var sk = Platform.Request.GetQueryStringParameter("sk");
if (!sk) {
Platform.Response.Redirect("/error?code=missing_sk", false);
}
var data = Platform.Function.Lookup("Subscribers", "Email", "SubscriberKey", sk);
Write("<p>Found: " + data + "</p>");
} catch (e) {
// In production: redirect to error page
// In development: show the error
var isDebug = Platform.Request.GetQueryStringParameter("debug") === "1";
if (isDebug) {
Write("<pre>Error: " + Platform.Function.Stringify(e) + "</pre>");
} else {
Platform.Response.Redirect("/error?code=unexpected", false);
}
}
</script>
Log Errors to a Data Extension
function logError(context, error) {
try {
Platform.Function.InsertData(
"ErrorLog",
"Timestamp", Platform.Function.Now(),
"Context", context,
"Message", error.message || Platform.Function.Stringify(error),
"PageURL", Platform.Request.GetQueryStringParameter("_url") || ""
);
} catch (logError) {
// Swallow logging errors to avoid infinite loop
}
}
try {
performOperation();
} catch (e) {
logError("performOperation", e);
Platform.Response.Redirect("/error");
}
HTTP Error Handling
try {
var req = new Script.Util.HttpRequest("https://api.example.com/data");
req.method = "GET";
req.continueOnError = true; // Don't throw on HTTP errors
req.retries = 2;
var resp = req.send();
// statusCode is a CLR value — Number() converts it so === works
var status = Number(resp.statusCode);
if (status === 200) {
var data = Platform.Function.ParseJSON(String(resp.content) + "");
// process data...
} else if (status === 401) {
throw new Error("Unauthorized — check your access token");
} else if (status === 404) {
throw new Error("Resource not found");
} else {
throw new Error("API error: " + status);
}
} catch (e) {
logError("apiCall", e);
Write('<p class="error">Could not load data. Please try again.</p>');
}
Validation Pattern
Guard against missing/invalid inputs early:
function validateInput(params) {
var errors = [];
if (!params.email) {
errors[errors.length] = "Email is required";
} else if (Platform.Function.IsEmailAddress(params.email) === false) {
errors[errors.length] = "Email is not valid";
}
if (!params.name) {
errors[errors.length] = "Name is required";
}
return errors;
}
var params = {
email: Platform.Request.GetFormField("email"),
name: Platform.Request.GetFormField("name")
};
var errors = validateInput(params);
if (errors.length > 0) {
Write('<ul class="errors">');
for (var i = 0; i < errors.length; i++) {
Write("<li>" + errors[i] + "</li>");
}
Write("</ul>");
} else {
// Process valid form
}
Show test script
<script runat="server">
/*
* Chapter: Common Error Patterns
* Proves:
* 1. null guard.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var result = null;
var safe = (result && result.Email) ? result.Email : "";
assert("guard", safe, "");
</script>
RaiseError
Platform.Function.RaiseError() is an SFMC-specific function that stops execution and logs an error. Unlike throw, it can optionally suppress the email send:
// In email context — stop execution (and optionally skip the send)
if (!subscriberEmail) {
Platform.Function.RaiseError("No email address found for subscriber", true);
// true = skip the send; false or omitted = continue the send job but stop this script
}
Use RaiseError in email contexts. Use throw + try/catch in CloudPage contexts.
Show test script
<script runat="server">
/*
* Chapter: RaiseError
* Proves:
* 1. Platform.Function.RaiseError exists as clrmethodinfo.
* NON-ASSERTABLE on CloudPage: calling RaiseError aborts the page (no PASS after).
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
assert("RaiseError typeof", typeof Platform.Function.RaiseError, "clrmethodinfo");
</script>
Debugging Errors
When you see a blank white page on a CloudPage, it’s usually an uncaught error. Enable debug mode:
// Add ?debug=1 to your URL during development
var isDebug = Platform.Request.GetQueryStringParameter("debug") === "1";
try {
// ... your code
} catch (e) {
if (isDebug) {
Write("<pre style='color:red'>" + Platform.Function.Stringify(e) + "</pre>");
} else {
Platform.Response.Redirect("/error", false);
}
}
See Debugging for more techniques.
Show test script
<script runat="server">
/*
* Chapter: Debugging Errors
* Proves:
* 1. String(e) usable for new Error.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
var e = new Error("boom");
assert("String(e) has boom", String(e).indexOf("boom") >= 0 ? "true" : "false", "true");
</script>