{"record":{"id":"5f2158751fafae1c","repo":"agalwood/Motrix","slug":"plugin-storage-value-not-serializable","errorCode":"plugin.storage.value_not_serializable","errorMessage":"plugin.storage.value_not_serializable: undefined is not JSON-serializable","messagePattern":"plugin\\.storage\\.value_not_serializable: undefined is not JSON-serializable","errorType":"exception","errorClass":"StorageError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/capabilities/storage.ts","lineNumber":78,"sourceCode":"  constructor(\n    public readonly code: string,\n    message: string\n  ) {\n    super(message)\n    this.name = 'StorageError'\n  }\n}\n\n/**\n * Serialize `value` to JSON.\n * Throws `plugin.storage.value_not_serializable` when:\n *  - the value is top-level `undefined`\n *  - JSON.stringify returns undefined (e.g. a plain function)\n *  - JSON.stringify throws (e.g. BigInt, class with throwing toJSON)\n */\nfunction serialize(value: unknown): string {\n  if (value === undefined) {\n    throw new StorageError(\n      'plugin.storage.value_not_serializable',\n      'plugin.storage.value_not_serializable: undefined is not JSON-serializable'\n    )\n  }\n  let json: string | undefined\n  try {\n    json = JSON.stringify(value)\n  } catch {\n    throw new StorageError(\n      'plugin.storage.value_not_serializable',\n      'plugin.storage.value_not_serializable: value cannot be serialized to JSON'\n    )\n  }\n  if (json === undefined) {\n    throw new StorageError(\n      'plugin.storage.value_not_serializable',\n      'plugin.storage.value_not_serializable: value cannot be serialized to JSON'\n    )","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/capabilities/storage.ts#L60-L96","documentation":"Storage capability mirror of 112. The top-level value passed to storage set() is undefined, and serialize() short-circuits with an explicit typed error rather than letting JSON.stringify silently drop it. Same value shape, different capability namespace (plugin.storage vs plugin.metadata).","triggerScenarios":"Calling storage.set(key, undefined); a variable that conditionally ended up undefined; refactor left a placeholder; plugin reads an optional env/config value and passes it through without a default.","commonSituations":"Plugin stores the result of an optional lookup; defaulting missing; TypeScript optional property accessed without nullish-coalescing.","solutions":["Use null to represent absence rather than undefined.","Skip the set() call when the value is undefined.","Default the value at the source with ?? null or ?? {}."],"exampleFix":"// before\nawait storage.set(key, maybeValue)\n\n// after\nif (maybeValue !== undefined) await storage.set(key, maybeValue)","handlingStrategy":"validation","validationCode":"if (value === undefined) throw new Error('storage value required')\nawait storage.set(key, value)","typeGuard":"function isDefined<T>(v: T | undefined): v is T { return v !== undefined }","tryCatchPattern":null,"preventionTips":["Represent absence with null rather than undefined in stored values.","Skip the set() call when the value is undefined.","Constrain settable values to a JSONValue type so undefined is rejected at compile time."],"tags":["storage","serialization","typescript","validation"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}