How to Serialize Errors in Javascript

Don't use a simple JSON.stringify

If you want to serialize an Error in Javascript you might be tempted to simply use JSON.stringify, but that won't be very helpful.

const error = new Error("Something went wrong");
const serialized = JSON.stringify(error);
// serialized -> '{}'

Normalize the error

Instead what I like to do is "normalize" the error (in case it's not actually an Error object) and then use the error message:

/**
 * @param {unknown} error
 * @returns {Error}
 */
export function normalizeError(error) {
  if (typeof error === "object" && error instanceof Error) {
    return error;
  } else if (typeof error === "string") {
    return new Error(error);
  }

  // else turn this unknown thing into a string
  return new Error(JSON.stringify(error));
}

// NOW I CAN CATCH ERRORS...
try {
  throw new Error("Something went wrong");
} catch (error) {
  const normalized = normalizeError(error);
  const serialized = normalized.message;
  // serialized -> "Something went wrong"
}

// AND ANYTHING ELSE
try {
  throw "Who throws a string? ME!";
} catch (error) {
  const normalized = normalizeError(error);
  const serialized = normalized.message;
  // serialized -> "Who throws a string? ME!"
}

Alternative: Use overloaded JSON.stringify

If you know you are dealing with an Error object (so you don't need to normalize it) then you can use JSON.stringify with a second argument to tell JSON stringify which properties to enumerate:

const error = new Error("Something went wrong");
const serialized = JSON.stringify(error, Object.getOwnPropertyNames(error));
// serialized -> '{"stack":"Error: Something went wrong\\n    at REPL9:1:13\\n    at Script.runInThisContext (node:vm:131:12)\\n    at REPLServer.defaultEval (node:repl:522:29)\\n    at bound (node:domain:416:15)\\n    at REPLServer.runBound [as eval] (node:domain:427:12)\\n    at REPLServer.onLine (node:repl:844:10)\\n    at REPLServer.emit (node:events:390:22)\\n    at REPLServer.EventEmitter.emit (node:domain:470:12)\\n    at REPLServer.Interface._onLine (node:readline:418:10)\\n    at REPLServer.Interface._line (node:readline:763:8)","message":"Something went wrong"}'

If you don't want the stack in there you can just list an array of the properties you want:

const error = new Error("Something went wrong");
const serialized = JSON.stringify(error, ["message", "name"]);
// serialized -> '{"message":"Something went wrong", "name": "Error"}'

Hopefully you found this post helpful, if you have any questions you can find me on Twitter.

Running Database Webhooks Locally with Supabase CLI
There is an App for That (But Shouldn't Be)