SFMC’s native error handling is minimal and hard to inspect. The most reliable way to debug production issues is to log errors yourself to a Data Extension.

Setting Up an Error Log DE

Create a DE with these columns:

Column Type Length Notes
LogId Text 36 Primary key, set to GUID()
Timestamp Date When the error occurred
Page Text 255 CloudPage name or script identifier
ErrorMessage Text 500 Short error message
ErrorStack Text 4000 Full stack trace
RequestData Text 4000 Query string, POST body snapshot
SubscriberKey Text 254 If available
Severity Text 20 "error", "warning", "info"

Basic Error Logger

function logError(page, message, stack, requestData, subKey) {
    try {
        Platform.Function.InsertData(
            "ErrorLog",
            ["LogId", "Timestamp", "Page", "ErrorMessage", "ErrorStack", "RequestData", "SubscriberKey", "Severity"],
            [
                Platform.Function.GUID(),
                Platform.Function.Now(),
                page || "unknown",
                (message || "").substring(0, 500),
                (stack || "").substring(0, 4000),
                (requestData || "").substring(0, 4000),
                subKey || "",
                "error"
            ]
        );
    } catch(e) {
        // Silently fail — don't error while logging an error
    }
}

Page-Level Error Wrapper

Wrap your entire CloudPage in a try/catch:

<script runat="server">
var PAGE_NAME = "checkout-form";
var requestSnapshot = "method=" + Platform.Request.Method
    + " qs=" + Platform.Request.GetResolvedURL();

try {
    Platform.Load("core", "1.1.5");
    var rawBody = Platform.Request.GetPostData();
    requestSnapshot += " body=" + (rawBody || "").substring(0, 200);

    // === Main page logic here ===

    var email = Platform.Request.GetFormField("email");
    if (!Platform.Function.IsEmailAddress(email)) {
        throw new Error("Invalid email: " + email);
    }

    // ... rest of logic ...

} catch(e) {
    logError(PAGE_NAME, e.message, e.stack, requestSnapshot, "");

    Platform.Response.SetResponseCode(500, "Internal Server Error");
    Platform.Response.SetContentType("application/json");
    Write(Stringify({ error: "An error occurred. Please try again." }));
}

function logError(page, message, stack, requestData, subKey) {
    try {
        Platform.Function.InsertData(
            "ErrorLog",
            ["LogId", "Timestamp", "Page", "ErrorMessage", "ErrorStack", "RequestData", "SubscriberKey", "Severity"],
            [Platform.Function.GUID(), Platform.Function.Now(), page,
             (message || "").substring(0, 500), (stack || "").substring(0, 4000),
             (requestData || "").substring(0, 4000), subKey || "", "error"]
        );
    } catch(e) {}
}
</script>

Severity Levels

Use different severity levels for different types of events:

function log(page, severity, message, data) {
    try {
        Platform.Function.InsertData(
            "AppLog",
            ["LogId", "Timestamp", "Page", "Severity", "Message", "Data"],
            [Platform.Function.GUID(), Platform.Function.Now(), page, severity,
             (message || "").substring(0, 500), Stringify(data || {}).substring(0, 2000)]
        );
    } catch(e) {}
}

log("orders", "info", "Order received", { orderId: "123", total: 99.99 });
log("checkout", "warning", "Duplicate submission detected", { email: email });
log("payment", "error", "Payment gateway timeout", { gateway: "stripe", elapsed: elapsed });

Automation Studio Logging

For automations, add structured step logging:

var JOB_ID = Platform.Function.GUID();
var SCRIPT_NAME = "nightly-sync";

function logStep(step, status, message, count) {
    Platform.Function.InsertData(
        "AutomationLog",
        ["JobId", "Script", "Step", "Status", "Message", "RowCount", "Timestamp"],
        [JOB_ID, SCRIPT_NAME, step, status, message || "", count || 0, Platform.Function.Now()]
    );
}

logStep("start", "running", "Script started", 0);
// ... process rows ...
logStep("retrieve", "ok", "Rows retrieved", rows.length);
// ... process each row ...
logStep("complete", "ok", "All rows processed", processedCount);

HTTP Error Logging

Log failed API calls with full context:

function callApi(url, method, payload, token) {
    var req = new Script.Util.HttpRequest(url);
    req.method = method || "GET";
    req.setHeader("Authorization", "Bearer " + token);
    if (payload) {
        req.contentType = "application/json";
        req.postData = Stringify(payload);
    }

    var resp;
    try {
        resp = req.send();
    } catch(e) {
        logError("api-call", "HTTP request failed: " + url, e.message, "", "");
        throw e;
    }

    if (resp.statusCode >= 400) {
        log("api-call", "error",
            "HTTP " + resp.statusCode + " from " + url,
            { url: url, status: resp.statusCode, body: String(resp.content).substring(0, 500) }
        );
    }

    return resp;
}

Monitoring with Email Alerts

For critical automation failures, send an alert email:

} catch(e) {
    logError(SCRIPT_NAME, e.message, e.stack, "", "");

    // Alert the ops team
    try {
        Platform.Load("core", "1.1.5");
        var ts = TriggeredSend.Init("AutomationError_TSD");
        ts.Send({
            EmailAddress: "ops@yourbrand.com",
            SubscriberKey: "ops-alert",
            Attributes: {
                ScriptName: SCRIPT_NAME,
                ErrorMessage: e.message,
                Timestamp: Platform.Function.Now()
            }
        });
    } catch(alertErr) {
        // Fail silently if alert itself fails
    }
}

See Also