{"record":{"id":"3d4a6d9614f0f1c3","repo":"can1357/oh-my-pi","slug":"value-is-not-json-serializable","errorCode":null,"errorMessage":"Value is not JSON-serializable","messagePattern":"Value is not JSON-serializable","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/json.ts","lineNumber":44,"sourceCode":"\tif (Array.isArray(value)) return value.map(stableJsonClone);\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst sorted = Object.create(null) as Record<string, unknown>;\n\t\tfor (const key of Object.keys(value).sort()) {\n\t\t\tsorted[key] = stableJsonClone(Reflect.get(value, key));\n\t\t}\n\t\treturn sorted;\n\t}\n\treturn value;\n}\n\n/**\n * Deterministically serialize JSON-shaped data by sorting object keys at every\n * depth while preserving array order. Throws for values JSON cannot represent\n * as a top-level value instead of returning an easy-to-misuse undefined.\n */\nexport function stableStringifyJson(value: unknown): string {\n\tconst serialized = JSON.stringify(stableJsonClone(value));\n\tif (serialized === undefined) throw new TypeError(\"Value is not JSON-serializable\");\n\treturn serialized;\n}\n","sourceCodeStart":26,"sourceCodeEnd":47,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/json.ts#L26-L47","documentation":"stableStringifyJson deterministically serializes JSON-shaped data (sorting object keys at every depth, preserving array order). JSON.stringify returns the string undefined when given a value that cannot be a top-level JSON value (undefined, a function, a symbol), so this wrapper converts that silent failure into an explicit TypeError rather than letting callers store/compare the string \"undefined\".","triggerScenarios":"Calling stableStringifyJson(undefined), stableStringifyJson(() => {...}), stableStringifyJson(Symbol('x')), or an object whose own toJSON returns undefined, e.g. stableStringifyJson({ toJSON: () => undefined }).","commonSituations":"Passing optional variables that are actually undefined into a cache-key/stable-hash helper, including callback functions in options objects that were meant to be stripped, and class instances with toJSON producing non-serializable values.","solutions":["Check the value before serializing: if (value === undefined) skip or substitute a placeholder like null.","Strip functions and symbols from the object before passing it (deep clean the data).","If undefined should serialize, wrap it explicitly, e.g. stableStringifyJson({ value: value ?? null }).","Catch the TypeError at call sites where the input is genuinely optional."],"exampleFix":"// before\nconst key = stableStringifyJson(options); // throws if options === undefined\n// after\nconst key = options === undefined ? \"null\" : stableStringifyJson(options);","handlingStrategy":"validation","validationCode":"function isSerializable(v: unknown): boolean {\n  return v !== undefined && typeof v !== \"function\" && typeof v !== \"symbol\";\n}","typeGuard":"function isJsonSerializable(v: unknown): v is string | number | boolean | null | object {\n  return !(v === undefined || typeof v === \"function\" || typeof v === \"symbol\");\n}","tryCatchPattern":"try {\n  return stableStringifyJson(value);\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"Value is not JSON-serializable\") {\n    logger.warn(\"stableStringifyJson got non-serializable value\", { type: typeof value });\n    return \"null\"; // or a sentinel key\n  }\n  throw err;\n}","preventionTips":["Default optional values: value ?? null before serializing.","Strip functions/symbols from options objects before hashing them into cache keys.","Don't pass raw option bags with callbacks to serialization helpers — pick the data fields explicitly.","Unit-test key-building with undefined inputs."],"tags":["json","serialization","type-error"],"backgroundTag":"non-serializable-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}