angular/angular-cli · error · InvalidUpdateRecordException

Invalid record instance.

Error message

Invalid record instance.

What it means

HostTree.commitUpdate only accepts records that are instanceof UpdateRecorderBase (the base class of UpdateRecorder). Passing anything else — a plain object, a recorder from an incompatible Tree implementation, or a non-recorder value — throws InvalidUpdateRecordException. This is a runtime type check on the commitUpdate API surface.

Source

Thrown at packages/angular_devkit/schematics/src/tree/host-tree.ts:409

      throw new FileDoesNotExistException(path);
    }

    return UpdateRecorderBase.createFromFileEntry(entry);
  }
  commitUpdate(record: UpdateRecorder): void {
    if (record instanceof UpdateRecorderBase) {
      const path = record.path;
      const entry = this.get(path);
      if (!entry) {
        throw new ContentHasMutatedException(path);
      } else {
        const newContent = record.apply(entry.content);
        if (!newContent.equals(entry.content)) {
          this.overwrite(path, newContent);
        }
      }
    } else {
      throw new InvalidUpdateRecordException();
    }
  }

  // Structural methods.
  create(path: string, content: Buffer | string): void {
    const c = typeof content == 'string' ? Buffer.from(content) : content;
    this._record.create(this._normalizePath(path), c as {} as virtualFs.FileBuffer).subscribe();
  }
  delete(path: string): void {
    this._recordSync.delete(this._normalizePath(path));
  }
  rename(from: string, to: string): void {
    this._recordSync.rename(this._normalizePath(from), this._normalizePath(to));
  }

  apply(action: Action, strategy?: MergeStrategy): void {
    throw new SchematicsException('Apply not implemented on host trees.');
  }

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Only pass the object returned by tree.beginUpdate(path) or tree.beginUpdate on the same tree instance.
  2. Check for duplicate @angular-devkit/schematics versions in node_modules (npm ls) and deduplicate so instanceof matches.
  3. Create custom recorders by extending UpdateRecorderBase, not by implementing the interface ad hoc.

Example fix

// before
tree.commitUpdate({ path, apply: c => c } as any);
// after
const record = tree.beginUpdate(filePath);
// ...edits...
tree.commitUpdate(record);
Defensive patterns

Strategy: type-guard

Validate before calling

import { UpdateRecorderBase } from '@angular-devkit/schematics/src/update/update-recorder';
const isCommittable = record instanceof UpdateRecorderBase;

Type guard

function isRealRecorder(r: unknown): r is UpdateRecorderBase {
  return r instanceof UpdateRecorderBase;
}

Try / catch

try {
  tree.commitUpdate(record);
} catch (e) {
  if ((e as Error).message === 'Invalid record instance.') {
    // record came from another tree/duplicate package: recreate via tree.beginUpdate
  } else throw e;
}

Prevention

When it happens

Trigger: Calling tree.commitUpdate(x) with a value not produced by tree.beginUpdate()/tree.recorder(); mixing recorders across different Tree implementations or across library versions where the class identity differs (e.g. two bundled copies of @angular-devkit/schematics); committing a mock or custom UpdateRecorder not extending UpdateRecorderBase.

Common situations: Duplicate @angular-devkit/schematics installations causing instanceof to fail even for 'real' recorders (same Jest instanceof pitfall noted in this file); passing the string returned from a wrong API; committing an already-committed or hand-rolled recorder object.

Related errors


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