denoland/deno · error · TypeError

Snapshot serializer must return a string

Error message

Snapshot serializer must return a string

What it means

assertSnapshot serializes the actual value with options.serializer (or the built-in serialize) and requires the result to be a string before comparing it to the stored snapshot (cli/js/40_test_snapshot.js). If a custom serializer returns a number, object, or undefined, this TypeError is thrown at assertion time. Note that JSON.stringify — a common serializer choice — returns undefined for top-level undefined, null is fine, but symbols/functions also stringify to undefined.

Solutions

  1. Make the serializer return a string: wrap the result with String(...) or JSON.stringify(...).
  2. Drop the custom serializer to use the built-in default, which always returns a string.
  3. After fixing the serializer, regenerate snapshots with `deno test --update` if the output format changed.

Example fix

// before
await t.assertSnapshot(state, { serializer: (s) => s.items.length });

// after
await t.assertSnapshot(state, { serializer: (s) => String(s.items.length) });
Defensive patterns

Strategy: type-guard

Validate before calling

// Wrap any custom serializer so it always returns a string:
function strSerializer(fn) {
  return (value) => {
    const out = fn(value);
    return typeof out === "string" ? out : JSON.stringify(out);
  };
}
await t.assertSnapshot(value, { serializer: strSerializer(mySerializer) });

Type guard

/** @param {unknown} s @returns {s is string} */
const isString = (s) => typeof s === "string";

Prevention

When it happens

Trigger: `t.assertSnapshot(v, { serializer: (x) => x.count })` (returns a number), `{ serializer: JSON.stringify }` when the value is undefined, or any serializer that forgets to stringify its result.

Common situations: Writing a compact custom serializer that extracts a field and forgets to convert it to a string; using JSON.stringify on values it cannot serialize at top level; changing a serializer's return type during a refactor.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/cf0f6909ccaee95f. Report an issue: GitHub.

Appendix: source

Thrown at cli/js/40_test_snapshot.js:337

    throw new TypeError(
      "Expected the second argument to assertSnapshot() to be an options object or a message string",
    );
  }

  const context = getSnapshotContext(options);
  const testName = options.name ?? getFullTestName(tContext);
  const count = (MapPrototypeGet(context.counts, testName) ?? 0) + 1;
  MapPrototypeSet(context.counts, testName, count);
  const name = `${testName} ${count}`;

  if (!ArrayPrototypeIncludes(context.updateQueue, name)) {
    ArrayPrototypePush(context.updateQueue, name);
  }

  const serializer = options.serializer ?? serialize;
  const actualSnapshot = serializer(actual);
  if (typeof actualSnapshot !== "string") {
    throw new TypeError("Snapshot serializer must return a string");
  }

  const expectedSnapshot = MapPrototypeGet(context.currentValues, name);

  if (getIsUpdateMode()) {
    if (actualSnapshot !== expectedSnapshot) {
      MapPrototypeSet(context.updatedValues, name, actualSnapshot);
      if (!ArrayPrototypeIncludes(context.updatedNames, name)) {
        ArrayPrototypePush(context.updatedNames, name);
      }
    }
    return;
  }

  if (!context.fileExists) {
    throw new AssertionError(
      getErrorMessage("Missing snapshot file.", options),
    );

View on GitHub (pinned to 9ad36f7a2c)