angular/angular-cli · error · Error

Invalid workspace file - expected JSON object.

Error message

Invalid workspace file - expected JSON object.

What it means

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.

Source

Thrown at packages/angular_devkit/core/src/workspace/json/reader.ts:53

export interface JsonWorkspaceOptions {
  allowedProjectExtensions?: string[];
  allowedWorkspaceExtensions?: string[];
}

export async function readJsonWorkspace(
  path: string,
  host: WorkspaceHost,
  options: JsonWorkspaceOptions = {},
): Promise<WorkspaceDefinition> {
  const raw = await host.readFile(path);
  if (raw === undefined) {
    throw new Error('Unable to read workspace file.');
  }

  const ast = parseTree(raw, undefined, { allowTrailingComma: true, disallowComments: false });
  if (ast?.type !== 'object' || !ast.children) {
    throw new Error('Invalid workspace file - expected JSON object.');
  }

  // Version check
  const versionNode = findNodeAtLocation(ast, ['version']);
  if (!versionNode) {
    throw new Error('Unknown format - version specifier not found.');
  }
  const version = versionNode.value;
  if (version !== 1) {
    throw new Error(`Invalid format version detected - Expected:[ 1 ] Found: [ ${version} ]`);
  }

  const context: ParserContext = {
    host,
    metadata: new JsonWorkspaceMetadata(path, ast, raw),
    trackChanges: true,
    unprefixedWorkspaceExtensions: new Set([
      ...ANGULAR_WORKSPACE_EXTENSIONS,

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Ensure the file's top level is a JSON object: { "version": 1, "projects": { ... } }.
  2. Restore the file from version control or regenerate it (ng new / ng generate config).
  3. Validate the JSON with a parser/linter before handing it to readWorkspace.
  4. Check for merge-conflict markers or truncated content and fix the file.

Example fix

// angular.json before
["projects"]
// angular.json after
{
  "version": 1,
  "projects": {}
}
Defensive patterns

Strategy: validation

Validate before calling

import { readFile } from 'fs/promises';
const raw = await readFile('angular.json', 'utf8');
const parsed = JSON.parse(raw);
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
  throw new Error('angular.json top level must be a JSON object');
}

Type guard

function isJsonObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Try / catch

try {
  const ws = await readWorkspace('angular.json', host);
} catch (e) {
  if (e.message.includes('expected JSON object')) {
    console.error('angular.json is malformed (top level is not an object). Restore from git or regenerate with `ng generate config`.');
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/7811092a4c72cb07. Report an issue: GitHub.