{"record":{"id":"2643c0a0f8021e8c","repo":"angular/angular-cli","slug":"name-must-be-a-json-value","errorCode":null,"errorMessage":"\"${name}\" must be a JSON value.","messagePattern":"\"(.+?)\" must be a JSON value\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/core/src/workspace/definitions.ts","lineNumber":182,"sourceCode":"        if (target) {\n          project.targets.set(name, target);\n        }\n      }\n    }\n\n    for (const [name, value] of Object.entries(definition)) {\n      switch (name) {\n        case 'name':\n        case 'root':\n        case 'sourceRoot':\n        case 'prefix':\n        case 'targets':\n          break;\n        default:\n          if (isJsonValue(value)) {\n            project.extensions[name] = value;\n          } else {\n            throw new TypeError(`\"${name}\" must be a JSON value.`);\n          }\n          break;\n      }\n    }\n\n    super.set(definition.name, project);\n\n    return project;\n  }\n\n  override set(name: string, value: ProjectDefinition): this {\n    this._validateName(name);\n\n    super.set(name, value);\n\n    return this;\n  }\n","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/core/src/workspace/definitions.ts#L164-L200","documentation":"In ProjectDefinitionCollection.add(), any property that is not one of the known keys (root, sourceRoot, prefix, targets, etc.) is treated as an extension and must be a JSON-serializable value (checked by isJsonValue). Passing functions, undefined, symbols, or other non-JSON values in the definition object throws this TypeError.","triggerScenarios":"Calling addProject/add with an extra property whose value is a function, class instance, undefined, symbol, or otherwise not JSON-representable, e.g. { name: 'app', root: '', onInit: () => {} }.","commonSituations":"Passing callbacks/objects into project definitions expecting them to be stored as extensions; leaking class instances from tooling into angular.json extensions; typos creating unexpected keys with non-JSON values.","solutions":["Only include JSON-serializable values (string, number, boolean, null, plain objects, arrays) for extension properties.","Move non-serializable behavior (functions, instances) out of the definition object into your own code.","Validate the definition object before calling add (strip unknown non-JSON keys)."],"exampleFix":"// before\nworkspace.addProject({ name: 'app', root: 'apps/app', hooks: { init: fn } });\n// after\nworkspace.addProject({ name: 'app', root: 'apps/app', hooksDescription: 'init handled externally' });","handlingStrategy":"type-guard","validationCode":"function isJsonValue(v: unknown): boolean {\n  return v === null || ['string', 'number', 'boolean'].includes(typeof v)\n    || (Array.isArray(v) && v.every(isJsonValue))\n    || (typeof v === 'object' && v !== null && !Array.isArray(v) && Object.values(v).every(isJsonValue));\n}\n// strip non-JSON extra keys before add\nconst extras = Object.fromEntries(Object.entries(def).filter(([, v]) => isJsonValue(v)));","typeGuard":"function isJsonValue(v: unknown): boolean {\n  if (v === null || ['string', 'number', 'boolean'].includes(typeof v)) return true;\n  if (Array.isArray(v)) return v.every(isJsonValue);\n  if (typeof v === 'object' && v !== null) return Object.values(v).every(isJsonValue);\n  return false;\n}","tryCatchPattern":"try {\n  project = workspace.addProject(def);\n} catch (e) {\n  if (e instanceof TypeError && /must be a JSON value/.test(e.message)) {\n    const { [e.message.match(/\"(.+)\"/)?.[1] ?? '']: _dropped, ...rest } = def;\n    project = workspace.addProject(rest);\n  } else throw e;\n}","preventionTips":["Never pass functions, class instances, undefined, or symbols in project definition objects","JSON.stringify-test extension values before adding them","Keep extension data limited to plain objects, arrays, strings, numbers, booleans, and null"],"tags":["workspace","json-serialization","typeerror","angular-devkit"],"backgroundTag":"non-serializable-value","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}