{"record":{"id":"bd278b22d7e1b953","repo":"angular/angular-cli","slug":"argument-isnonprimitive-arg-arg-tostring","errorCode":null,"errorMessage":"Argument ${isNonPrimitive(arg) ? arg.toString() : arg} is JSON serializable.","messagePattern":"Argument (.+?) is JSON serializable\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/cli/src/utilities/memoize.ts","lineNumber":29,"sourceCode":" *\n * **Note**: Be cautious where and how to use this decorator as the size of the cache will grow unbounded.\n *\n * @see https://en.wikipedia.org/wiki/Memoization\n */\nexport function memoize<This, Args extends unknown[], Return>(\n  target: (this: This, ...args: Args) => Return,\n  context: ClassMemberDecoratorContext,\n) {\n  if (context.kind !== 'method' && context.kind !== 'getter') {\n    throw new Error('Memoize decorator can only be used on methods or get accessors.');\n  }\n\n  const cache = new Map<string, Return>();\n\n  return function (this: This, ...args: Args): Return {\n    for (const arg of args) {\n      if (!isJSONSerializable(arg)) {\n        throw new Error(\n          `Argument ${isNonPrimitive(arg) ? arg.toString() : arg} is JSON serializable.`,\n        );\n      }\n    }\n\n    const key = JSON.stringify(args);\n    if (cache.has(key)) {\n      return cache.get(key) as Return;\n    }\n\n    const result = target.apply(this, args);\n    cache.set(key, result);\n\n    return result;\n  };\n}\n\n/** Method to check if value is a non primitive. */","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/cli/src/utilities/memoize.ts#L11-L47","documentation":"`memoize` builds its cache key with `JSON.stringify(args)`, so every argument must be JSON-serializable. Before calling the wrapped function it checks each argument with `isJSONSerializable` and throws this error for any non-serializable argument. (The message text reads 'is JSON serializable' but the intent is that the argument must be.)","triggerScenarios":"Calling a `@memoize`-decorated method/getter with arguments that fail `isJSONSerializable` — objects with circular references, class instances with functions/Symbols, `undefined`, functions, DOM nodes, Buffers, etc.","commonSituations":"Passing `this`-bound objects, HttpParams/Request objects, or class instances as arguments to memoized methods; passing `undefined` or callback functions; memoizing methods that take complex runtime objects instead of primitive IDs.","solutions":["Pass only JSON-serializable arguments (strings, numbers, booleans, plain objects/arrays) to memoized methods.","Refactor to pass a stable primitive key (e.g. an id or path) instead of an object, and look up the object inside the method.","Drop the `@memoize` decorator and implement an explicit cache keyed appropriately for complex arguments.","Sanitize arguments before the call (strip circular refs/functions) if they are only used as keys."],"exampleFix":"// before\n@memoize loadUser(user: User) {...}\nservice.loadUser(someUserInstance); // throws\n// after\n@memoize loadUser(userId: string) {...}\nservice.loadUser(someUserInstance.id);","handlingStrategy":"validation","validationCode":"function isJsonSerializable(v: unknown): boolean {\n  try { JSON.stringify(v); return true; } catch { return false; }\n}\nif (!isJsonSerializable(arg)) throw new TypeError('Argument must be JSON-serializable for memoized methods');","typeGuard":"function isJsonSerializableArg(v: unknown): v is string | number | boolean | null | JsonValue {\n  try { JSON.stringify(v); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  memoizedService.load(complexObject);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('is JSON serializable')) {\n    // retry with a primitive key instead\n    memoizedService.load(complexObject.id);\n  } else { throw e; }\n}","preventionTips":["Pass primitives (ids, paths) to memoized methods, not object instances.","Avoid functions, Symbols, undefined, and circular structures as arguments.","Remember the cache key is JSON.stringify(args) — keep args small and stable.","Remove @memoize and use an explicit cache if arguments are inherently non-serializable."],"tags":["typescript","memoize","serialization","cache"],"backgroundTag":"non-serializable-argument","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}