{"record":{"id":"7811092a4c72cb07","repo":"angular/angular-cli","slug":"invalid-workspace-file-expected-json-object","errorCode":null,"errorMessage":"Invalid workspace file - expected JSON object.","messagePattern":"Invalid workspace file - expected JSON object\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/core/src/workspace/json/reader.ts","lineNumber":53,"sourceCode":"\nexport interface JsonWorkspaceOptions {\n  allowedProjectExtensions?: string[];\n  allowedWorkspaceExtensions?: string[];\n}\n\nexport async function readJsonWorkspace(\n  path: string,\n  host: WorkspaceHost,\n  options: JsonWorkspaceOptions = {},\n): Promise<WorkspaceDefinition> {\n  const raw = await host.readFile(path);\n  if (raw === undefined) {\n    throw new Error('Unable to read workspace file.');\n  }\n\n  const ast = parseTree(raw, undefined, { allowTrailingComma: true, disallowComments: false });\n  if (ast?.type !== 'object' || !ast.children) {\n    throw new Error('Invalid workspace file - expected JSON object.');\n  }\n\n  // Version check\n  const versionNode = findNodeAtLocation(ast, ['version']);\n  if (!versionNode) {\n    throw new Error('Unknown format - version specifier not found.');\n  }\n  const version = versionNode.value;\n  if (version !== 1) {\n    throw new Error(`Invalid format version detected - Expected:[ 1 ] Found: [ ${version} ]`);\n  }\n\n  const context: ParserContext = {\n    host,\n    metadata: new JsonWorkspaceMetadata(path, ast, raw),\n    trackChanges: true,\n    unprefixedWorkspaceExtensions: new Set([\n      ...ANGULAR_WORKSPACE_EXTENSIONS,","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/core/src/workspace/json/reader.ts#L35-L71","documentation":"After reading, the parser (parseTree with trailing-comma support) must produce an object-typed AST with children. If the file parses to a non-object (array, string, number) or fails to produce a usable tree, the reader throws this Error because the workspace file format requires a top-level JSON object.","triggerScenarios":"angular.json containing '[...]' or a bare value like '\"text\"' or '123' at the top level; a file that is valid JSON but not an object; content that parses to nothing usable.","commonSituations":"Hand-edited angular.json that replaced the object with an array; file overwritten by a tool dumping a config list; merge conflicts resolved to non-object content; BOM/encoding issues producing an unparseable tree.","solutions":["Ensure the file's top level is a JSON object: { \"version\": 1, \"projects\": { ... } }.","Restore the file from version control or regenerate it (ng new / ng generate config).","Validate the JSON with a parser/linter before handing it to readWorkspace.","Check for merge-conflict markers or truncated content and fix the file."],"exampleFix":"// angular.json before\n[\"projects\"]\n// angular.json after\n{\n  \"version\": 1,\n  \"projects\": {}\n}","handlingStrategy":"validation","validationCode":"import { readFile } from 'fs/promises';\nconst raw = await readFile('angular.json', 'utf8');\nconst parsed = JSON.parse(raw);\nif (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n  throw new Error('angular.json top level must be a JSON object');\n}","typeGuard":"function isJsonObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const ws = await readWorkspace('angular.json', host);\n} catch (e) {\n  if (e.message.includes('expected JSON object')) {\n    console.error('angular.json is malformed (top level is not an object). Restore from git or regenerate with `ng generate config`.');\n  } else throw e;\n}","preventionTips":["Never hand-edit angular.json's top-level structure to a non-object","Resolve merge conflicts fully — search for <<<<<<< markers before parsing","Lint angular.json as JSON in CI to catch truncation/structure damage","Keep the file under version control so it can be restored quickly"],"tags":["workspace","json-parse","malformed-config","angular-devkit"],"backgroundTag":"malformed-json","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}