{"record":{"id":"cf0f6909ccaee95f","repo":"denoland/deno","slug":"snapshot-serializer-must-return-a-string","errorCode":null,"errorMessage":"Snapshot serializer must return a string","messagePattern":"Snapshot serializer must return a string","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"cli/js/40_test_snapshot.js","lineNumber":337,"sourceCode":"    throw new TypeError(\n      \"Expected the second argument to assertSnapshot() to be an options object or a message string\",\n    );\n  }\n\n  const context = getSnapshotContext(options);\n  const testName = options.name ?? getFullTestName(tContext);\n  const count = (MapPrototypeGet(context.counts, testName) ?? 0) + 1;\n  MapPrototypeSet(context.counts, testName, count);\n  const name = `${testName} ${count}`;\n\n  if (!ArrayPrototypeIncludes(context.updateQueue, name)) {\n    ArrayPrototypePush(context.updateQueue, name);\n  }\n\n  const serializer = options.serializer ?? serialize;\n  const actualSnapshot = serializer(actual);\n  if (typeof actualSnapshot !== \"string\") {\n    throw new TypeError(\"Snapshot serializer must return a string\");\n  }\n\n  const expectedSnapshot = MapPrototypeGet(context.currentValues, name);\n\n  if (getIsUpdateMode()) {\n    if (actualSnapshot !== expectedSnapshot) {\n      MapPrototypeSet(context.updatedValues, name, actualSnapshot);\n      if (!ArrayPrototypeIncludes(context.updatedNames, name)) {\n        ArrayPrototypePush(context.updatedNames, name);\n      }\n    }\n    return;\n  }\n\n  if (!context.fileExists) {\n    throw new AssertionError(\n      getErrorMessage(\"Missing snapshot file.\", options),\n    );","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/js/40_test_snapshot.js#L319-L355","documentation":"Thrown by assertSnapshot() (cli/js/40_test_snapshot.js) when the serializer applied to the actual value does not return a string. The default serializer (serialize) always yields a string; a custom options.serializer must too, because snapshot files store plain string entries keyed by test name and counter.","triggerScenarios":"Passing { serializer: (v) => v } (identity on a non-string), a serializer returning undefined/null on some path (early return), or one returning an object/number like (v) => JSON.stringify(v, null, 2) missing the stringify. Fires per assertion, before any comparison.","commonSituations":"Writing a custom serializer that formats only the expected input shape and silently returns undefined for edge cases; reusing a formatter that returns numbers for numeric values; refactoring a serializer to return structured data instead of text.","solutions":["Make the serializer total: coerce the final result with String(...) or JSON.stringify(...)","Handle every branch — no early returns that drop the value","Unit-test the serializer directly against the same shapes your snapshots capture"],"exampleFix":"// before\nawait t.assertSnapshot(data, {\n  serializer: (v) => v.items.map((i) => i.name).join(\"\\n\") && v.items.length\n    ? v.items.map((i) => i.name).join(\"\\n\")\n    : undefined,\n});\n\n// after\nawait t.assertSnapshot(data, {\n  serializer: (v) => v.items.map((i) => i.name).join(\"\\n\") ?? \"\",\n});","handlingStrategy":"type-guard","validationCode":"function makeSerializer(fn) {\n  return (value) => {\n    const out = fn(value);\n    if (typeof out !== \"string\") {\n      throw new TypeError(`serializer returned ${typeof out}; wrap with String()`);\n    }\n    return out;\n  };\n}\n// use: t.assertSnapshot(v, { serializer: makeSerializer(fmt) })","typeGuard":"/** @param {(v: unknown) => unknown} fn */\nfunction returnsString(fn, sample) {\n  return typeof fn(sample) === \"string\";\n}","tryCatchPattern":null,"preventionTips":["End every custom serializer with an explicit String(...) or JSON.stringify(...) coercion","Unit-test serializers against the same value shapes the snapshots capture before running the suite"],"tags":["testing","snapshot","serializer","typeerror"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}