Syntax

new Error([message])
0–1 arguments

Parameters

Name Type Required Description
message string No Human-readable description of the error. After new Error(msg) it is not readable via .message (use String(e)). After call-form Error(msg) or on engine-raised errors, .message is set.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Parameters — the optional `message` argument
 *
 * Proves:
 *   1. Error accepts 0 or 1 arguments; both forms return an object.
 *   2. After new Error(msg) the text is NOT readable via .message —
 *      it is undefined and not an own property (MDN: .message === msg).
 *   3. The documented recovery: String(e) and ("" + e) return the text.
 *   4. After call-form Error(msg) the .message IS set, and it is an own
 *      property.
 *   5. Engine-raised errors also carry .message.
 *   6. With no argument: new Error() leaves .message undefined, while
 *      call-form Error() still sets it — to the empty string.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    var got;
    try { got = String(actual); } catch (ex) { got = "THREW"; }
    Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 1. Arity — 0 and 1 argument both produce an object. */
var noArg = new Error();
assert("typeof new Error() is object", typeof noArg, "object");
var oneArg = new Error("boom");
assert("typeof new Error('boom') is object", typeof oneArg, "object");
var callNoArg = Error();
assert("typeof Error() call-form is object", typeof callNoArg, "object");

/* 2. DEVIATION — new Error(msg) does not populate .message. */
assert("DEV typeof new Error('boom').message is undefined (MDN: 'boom')", typeof oneArg.message, "undefined");
assert("DEV new Error('boom').hasOwnProperty('message') is false (MDN: true)", oneArg.hasOwnProperty("message"), "false");

/* 3. Documented recovery of the text. */
assert("workaround String(new Error('boom')) is 'boom'", String(oneArg), "boom");
assert("workaround ('' + new Error('boom')) is 'boom'", "" + oneArg, "boom");

/* 4. Call-form DOES set .message. */
var callForm = Error("via-call");
assert("Error('via-call').message is 'via-call'", callForm.message, "via-call");
assert("Error('via-call').hasOwnProperty('message') is true", callForm.hasOwnProperty("message"), "true");
assert("String(Error('via-call')) is 'via-call'", String(callForm), "via-call");

/* 5. Engine-raised errors carry .message. */
var engineMsgType = "not-thrown";
try {
    Platform.Function.Lookup("NonExistentDE_ForTest", "Field", "Key", "value");
} catch (ex) {
    engineMsgType = typeof ex.message;
}
assert("engine-raised error exposes a string .message", engineMsgType, "string");

/* 6. No-argument forms leave .message undefined. */
assert("typeof new Error().message is undefined", typeof noArg.message, "undefined");
/* Call-form always sets .message; with no argument it is the empty string. */
assert("Error() call-form sets .message to the empty string", callNoArg.message, "");
assert("Error() call-form .message is a string", typeof callNoArg.message, "string");
</script>

Description

Error is the native JavaScript Error constructor. Use it with throw and try/catch for structured error handling in SSJS.

Show test script — message shape, toString and instanceof deviations
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Description + differs-from-MDN callout
 *
 * MDN / ECMAScript says:
 *   - new Error("msg").message === "msg"
 *   - new Error("msg").toString() === "Error: msg"
 *   - (new Error()) instanceof Error === true
 *
 * SFMC (Jint) actually does:
 *   1. new Error("msg") leaves .message undefined — recover with String(e)
 *      or ("" + e).
 *   2. Call-form Error("msg") DOES set .message.
 *   3. Engine-raised errors set BOTH .message and .description.
 *   4. e.toString() after new Error("msg") returns "Error: undefined".
 *   5. instanceof Error is always false — use e.constructor === Error
 *      or e.name instead.
 *
 * Also proves the base shape: Error is a function, .name is "Error",
 * and the object is throwable/catchable.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    var got;
    try { got = String(actual); } catch (ex) { got = "THREW"; }
    Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 0. Base shape. */
assert("typeof Error is function", typeof Error, "function");
var e = new Error("msg");
assert("new Error('msg').name is 'Error'", e.name, "Error");

/* 1. DEVIATION — .message is not populated by the new-form. */
assert("DEV typeof new Error('msg').message is undefined (MDN: 'msg')", typeof e.message, "undefined");
assert("DEV new Error('msg').hasOwnProperty('message') is false (MDN: true)", e.hasOwnProperty("message"), "false");
assert("workaround String(e) is 'msg'", String(e), "msg");
assert("workaround ('' + e) is 'msg'", "" + e, "msg");

/* 2. Call-form does set .message. */
var c = Error("msg2");
assert("Error('msg2').message is 'msg2'", c.message, "msg2");

/* 3. Engine-raised errors carry .message AND .description. */
var engMsg = "none", engDesc = "none";
try {
    Platform.Function.Lookup("NonExistentDE_ForTest", "Field", "Key", "value");
} catch (ex) {
    engMsg = typeof ex.message;
    engDesc = typeof ex.description;
}
assert("engine-raised error .message is a string", engMsg, "string");
assert("engine-raised error .description is a string", engDesc, "string");

/* 4. DEVIATION — toString() after new loses the message. */
assert("DEV new Error('msg').toString() is 'Error: undefined' (MDN: 'Error: msg')", e.toString(), "Error: undefined");
assert("Error('msg2').toString() is 'Error: msg2'", c.toString(), "Error: msg2");

/* 5. DEVIATION — instanceof Error is always false. */
assert("DEV new Error('msg') instanceof Error is false (MDN: true)", e instanceof Error, "false");
assert("DEV Error('msg2') instanceof Error is false (MDN: true)", c instanceof Error, "false");
assert("workaround e.constructor === Error is true", e.constructor === Error, "true");
assert("workaround e.name is 'Error'", e.name, "Error");

/* 6. instanceof stays false even after throw/catch. */
var caughtInstance = "not-run", caughtName = "not-run";
try {
    throw new Error("thrown");
} catch (ex) {
    caughtInstance = ex instanceof Error;
    caughtName = ex.name;
}
assert("DEV caught new Error instanceof Error is false (MDN: true)", caughtInstance, "false");
assert("caught new Error .name is 'Error'", caughtName, "Error");
</script>

Examples

Basic throw and catch

try {
    throw new Error("Something went wrong");
} catch (e) {
    // e.message is undefined after new Error — use String(e).
    Write(String(e)); // "Something went wrong"
}

Call-form vs new (message shape)

var withNew = new Error("via-new");
Write(withNew.message);   // undefined
Write(String(withNew));   // "via-new"

var callForm = Error("via-call");
Write(callForm.message);  // "via-call"
Write(String(callForm));  // "via-call"

Conditional error

function getSubscriberEmail(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("No subscriber found for key: " + sk);
    }

    return email;
}

try {
    var email = getSubscriberEmail(subscriberKey);
    Write("<p>Email: " + email + "</p>");
} catch (e) {
    // String(e) recovers the thrown message; e.message would be undefined here.
    Write('<p class="error">' + String(e) + "</p>");
}

HTTP error handling

try {
    var req = new Script.Util.HttpRequest("https://api.example.com/data");
    req.method = "GET";
    req.continueOnError = true;
    var resp = req.send();

    // statusCode is a CLR value — Number() converts it so === works
    var status = Number(resp.statusCode);
    if (status === 401) {
        throw new Error("Unauthorized: check your access token");
    } else if (status !== 200) {
        throw new Error("API returned status " + status);
    }

    var data = Platform.Function.ParseJSON(String(resp.content) + "");
    // process data...

} catch (e) {
    // Use String(e) — e.message is undefined for new Error(...).
    Platform.Function.InsertData("ErrorLog", "Message", String(e), "Timestamp", Platform.Function.Now());
    Platform.Response.Redirect("/error", false);
}

Error in serialization

Stringify(e) behaves differently depending on how the error was created:

  • For new Error(...), Stringify(e) is often {} when not thrown, or surfaces a hidden {"jintException": ...} after throw/catchnot the message. Use String(e) to log the message.
  • For call-form Error("msg"), Stringify(e) yields {"message":"msg"}.
  • For an engine-raised error, Stringify(e) yields {"message": ..., "description": ...}.
try {
    performOperation();
} catch (e) {
    // For engine-raised errors this includes message + description;
    // for new Error(...) prefer String(e) to capture the message.
    Write("<pre>Error details: " + String(e) + " | " + Stringify(e) + "</pre>");
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Examples
 *
 * Proves every commented result in the chapter's example blocks:
 *   1. Basic throw/catch — String(e) is "Something went wrong".
 *   2. Call-form vs new:
 *        new Error("via-new").message  -> undefined
 *        String(new Error("via-new"))  -> "via-new"
 *        Error("via-call").message     -> "via-call"
 *        String(Error("via-call"))     -> "via-call"
 *   3. Conditional error — a guard clause throws and the catch recovers the
 *      message text via String(e).
 *   4. Error in serialization:
 *        Stringify(new Error(...)) not thrown  -> "{}" (NOT the message)
 *        Stringify(Error("msg"))               -> {"message":"msg"}
 *        Stringify(engine-raised)              -> contains message + description
 *        Stringify of a CAUGHT new Error       -> does not contain the message
 *
 * The HTTP example is illustrative only (live HTTP calls time a CloudPage
 * out); its throw/catch shape is covered by points 1 and 3.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    var got;
    try { got = String(actual); } catch (ex) { got = "THREW"; }
    Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
    var threw = false;
    try { fn(); } catch (ex) { threw = true; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw" : "did NOT throw") + "\n");
}

/* 1. Basic throw and catch. */
var basic = "not-run";
try {
    throw new Error("Something went wrong");
} catch (ex) {
    basic = String(ex);
}
assert("String(e) after throw new Error is 'Something went wrong'", basic, "Something went wrong");

/* 2. Call-form vs new. */
var withNew = new Error("via-new");
assert("new Error('via-new').message is undefined", typeof withNew.message, "undefined");
assert("String(new Error('via-new')) is 'via-new'", String(withNew), "via-new");
var callForm = Error("via-call");
assert("Error('via-call').message is 'via-call'", callForm.message, "via-call");
assert("String(Error('via-call')) is 'via-call'", String(callForm), "via-call");

/* 3. Conditional error — guard clause. */
function getValue(sk) {
    if (!sk) {
        throw new Error("SubscriberKey is required");
    }
    return sk;
}
assertThrows("guard clause throws when the argument is missing", function () { return getValue(""); });
var guardText = "not-run";
try {
    getValue("");
} catch (ex) {
    guardText = String(ex);
}
assert("caught guard message is 'SubscriberKey is required'", guardText, "SubscriberKey is required");
assert("guard returns the value when present", getValue("abc"), "abc");

/* 4. Error in serialization. */
var notThrown = new Error("serialize-me");
assert("Stringify(new Error(...)) not thrown is '{}'", Stringify(notThrown), "{}");
var callErr = Error("msg");
assert("Stringify(Error('msg')) is the message object", Stringify(callErr), '{"message":"msg"}');

var caughtDump = "not-run";
try {
    throw new Error("hidden-text");
} catch (ex) {
    caughtDump = Stringify(ex);
}
assert("Stringify of a caught new Error does NOT contain the message", caughtDump.indexOf("hidden-text") === -1, "true");

var engDump = "not-run";
try {
    Platform.Function.Lookup("NonExistentDE_ForTest", "Field", "Key", "value");
} catch (ex) {
    engDump = Stringify(ex);
}
assert("Stringify(engine-raised) contains 'message'", engDump.indexOf("message") > -1, "true");
assert("Stringify(engine-raised) contains 'description'", engDump.indexOf("description") > -1, "true");
</script>

Notes

The error object in SSJS is similar to but not identical to the standard ECMAScript Error object, and its shape depends on origin:

  • new Error("msg"): .message is undefined (not own); recover via String(e) / ("" + e). .name is "Error". .stack is unavailable. instanceof Error is false; e.constructor === Error is true.
  • Call-form Error("msg"): .message is set to the argument; hasOwnProperty("message") is true; toString() returns "Error: msg".
  • Engine-raised (platform-thrown, e.g. a bad Platform.Function call): exposes .message (short) and .description (fuller text). .stack is unavailable. instanceof Error / instanceof TypeError are still false.
  • Thrown primitives / plain objects (throw "text", throw { message: ..., description: ... }) are valid; in catch (e), e may be a string, a plain object, or an engine error — probe accordingly. Avoid String(e) on a thrown plain object (it can throw a .NET null-reference); prefer Stringify(e) or e.toString() there.
  • for (var k in e) is unreliable for property discovery: on engine errors, JS Error instances, and thrown strings it enumerates the message characters, not property names.

SFMC platform errors (not thrown by your code) are also catchable, and these DO carry .message:

try {
    Platform.Function.Lookup("NonExistentDE", "Field", "Key", "value");
} catch (e) {
    // Engine-raised errors expose .message and .description.
    Write("Platform error: " + e.message);
}
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Notes — the error object's shape depends on its origin
 *
 * Proves, per origin:
 *   1. new Error("msg"): .message undefined (not own), recover via String(e)
 *      / ("" + e); .name is "Error"; .stack unavailable;
 *      instanceof Error is false; e.constructor === Error is true.
 *   2. Call-form Error("msg"): .message set, hasOwnProperty("message") true,
 *      toString() is "Error: msg".
 *   3. Engine-raised: .message (short) + .description (fuller); .stack
 *      unavailable; instanceof Error and instanceof TypeError both false.
 *   4. Thrown primitives / plain objects are valid — catch receives a string
 *      or a plain object; on a thrown plain object prefer Stringify(e) /
 *      e.toString() over String(e).
 *   5. for (var k in e) enumerates the message CHARACTERS, not property
 *      names, for engine errors / JS Error instances / thrown strings.
 *   6. SFMC platform errors are catchable and DO carry .message.
 *
 * All "DEV" lines name the MDN/spec expectation inline.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    var got;
    try { got = String(actual); } catch (ex) { got = "THREW"; }
    Platform.Response.Write((got === String(expected) ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}

/* 1. new Error("msg"). */
var e = new Error("msg");
assert("DEV new Error('msg').message is undefined (MDN: 'msg')", typeof e.message, "undefined");
assert("DEV new Error('msg') has no own 'message' (MDN: own property)", e.hasOwnProperty("message"), "false");
assert("String(new Error('msg')) recovers the text", String(e), "msg");
assert("('' + new Error('msg')) recovers the text", "" + e, "msg");
assert("new Error('msg').name is 'Error'", e.name, "Error");
assert("DEV new Error('msg').stack is unavailable (MDN: a string)", typeof e.stack, "undefined");
assert("DEV new Error('msg') instanceof Error is false (MDN: true)", e instanceof Error, "false");
assert("new Error('msg').constructor === Error is true", e.constructor === Error, "true");

/* 2. Call-form Error("msg"). */
var c = Error("msg");
assert("Error('msg').message is 'msg'", c.message, "msg");
assert("Error('msg').hasOwnProperty('message') is true", c.hasOwnProperty("message"), "true");
assert("Error('msg').toString() is 'Error: msg'", c.toString(), "Error: msg");

/* 3. Engine-raised error. */
var engMsgType = "none", engDescType = "none", engStack = "none";
var engIsError = "none", engIsTypeError = "none";
try {
    Platform.Function.Lookup("NonExistentDE_ForTest", "Field", "Key", "value");
} catch (ex) {
    engMsgType = typeof ex.message;
    engDescType = typeof ex.description;
    engStack = typeof ex.stack;
    engIsError = ex instanceof Error;
    engIsTypeError = ex instanceof TypeError;
}
assert("engine-raised .message is a string", engMsgType, "string");
assert("engine-raised .description is a string", engDescType, "string");
assert("DEV engine-raised .stack is unavailable (MDN: a string)", engStack, "undefined");
assert("DEV engine-raised instanceof Error is false (MDN: true)", engIsError, "false");
assert("DEV engine-raised instanceof TypeError is false", engIsTypeError, "false");

/* 4. Thrown primitives and plain objects. */
var thrownStr = "not-run", thrownStrType = "not-run";
try {
    throw "plain text";
} catch (ex) {
    thrownStrType = typeof ex;
    thrownStr = String(ex);
}
assert("throw 'text' is catchable and typeof e is string", thrownStrType, "string");
assert("caught thrown string equals the thrown text", thrownStr, "plain text");

var objType = "not-run", objMsg = "not-run", objDump = "not-run";
try {
    throw { message: "obj-msg", description: "obj-desc" };
} catch (ex) {
    objType = typeof ex;
    objMsg = ex.message;
    objDump = Stringify(ex);
}
assert("throw {plain object} is catchable and typeof e is object", objType, "object");
assert("caught plain object keeps .message", objMsg, "obj-msg");
assert("Stringify(thrown plain object) contains the description", objDump.indexOf("obj-desc") > -1, "true");

/* 5. for..in enumerates the message CHARACTERS, not property names. */
var keys = [];
for (var k in e) { keys.push(k); }
assert("DEV for..in over a JS Error yields characters, not property names", keys.join(""), "msg");
assert("DEV for..in key count equals the message length, not 1", keys.length, 3);

var strKeys = [];
var thrownText = "";
try { throw "abcd"; } catch (ex) { thrownText = ex; for (var k2 in ex) { strKeys.push(k2); } }
assert("DEV for..in over a thrown string yields its characters", strKeys.join(""), "abcd");

/* 6. SFMC platform errors are catchable and expose .message. */
var platMsg = "not-run";
try {
    Platform.Function.Lookup("NonExistentDE_ForTest", "Field", "Key", "value");
} catch (ex) {
    platMsg = ex.message;
}
assert("platform error .message is a non-empty string", platMsg.length > 0, "true");
</script>

See Also