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
- Only pass the object returned by tree.beginUpdate(path) or tree.beginUpdate on the same tree instance.
- Check for duplicate @angular-devkit/schematics versions in node_modules (npm ls) and deduplicate so instanceof matches.
- 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
- Only commit recorders obtained from the same tree's beginUpdate/recorder
- Check `npm ls @angular-devkit/schematics` for duplicate versions that break instanceof
- Never hand-roll objects cast to UpdateRecorder
- Use tree.recorder(path) convenience API instead of custom recorders
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
- Content at path "${path}" has changed between the start and
- Tree type is not supported.
- schematicName cannot be undefined.
- The "not" keyword is not supported in JSON Schema.
- Could not find (/.angular.json)
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/19df54d34c04fe11.
Report an issue: GitHub.