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>