angular/angular-cli · error · IndexOutOfBoundException
Index ${index} outside of range [0, ${this.content.original.
Error message
Index ${index} outside of range [0, ${this.content.original.length}]. What it means
UpdateRecorderBase._assertIndex validates that every edit position falls within [0, fileLength] before recording an insert or removal; otherwise it throws IndexOutOfBoundException. It fires when insertLeft, insertRight, or remove are called with an index beyond the file's original content length.
Source
Thrown at packages/angular_devkit/schematics/src/tree/recorder.ts:68
// Check if we're BOM.
if (c0 == 0xef && c1 == 0xbb && c2 == 0xbf) {
return new UpdateRecorderBase(entry.content, entry.path, 'utf-8', true);
} else if (c0 === 0xff && c1 == 0xfe) {
return new UpdateRecorderBase(entry.content, entry.path, 'utf-16le', true);
} else if (c0 === 0xfe && c1 == 0xff) {
return new UpdateRecorderBase(entry.content, entry.path, 'utf-16be', true);
}
return new UpdateRecorderBase(entry.content, entry.path);
}
get path(): string {
return this._path;
}
protected _assertIndex(index: number): void {
if (index < 0 || index > this.content.original.length) {
throw new IndexOutOfBoundException(index, 0, this.content.original.length);
}
}
// These just record changes.
insertLeft(index: number, content: Buffer | string): UpdateRecorder {
this._assertIndex(index);
this.content.appendLeft(index, content.toString());
return this;
}
insertRight(index: number, content: Buffer | string): UpdateRecorder {
this._assertIndex(index);
this.content.appendRight(index, content.toString());
return this;
}
View on GitHub (pinned to bb72145f9a)
Solutions
- Clamp or validate the index: only call insert/remove when 0 <= index <= (tree.read(path)?.length ?? 0).
- Recompute offsets from the current file content right before editing instead of caching them.
- Handle search misses (indexOf === -1) by throwing a descriptive error instead of using the index.
Example fix
// before
const idx = content.indexOf(marker); // -1 on miss
recorder.insertLeft(idx, snippet);
// after
const idx = content.indexOf(marker);
if (idx === -1 || idx > content.length) {
throw new SchematicsException(`Marker not found in ${path}`);
}
recorder.insertLeft(idx, snippet); Defensive patterns
Strategy: validation
Validate before calling
function canEditAt(buf: Buffer | null, index: number): boolean {
return buf !== null && Number.isInteger(index) && index >= 0 && index <= buf.length;
} Type guard
function isValidIndex(index: number, content: Buffer | string): boolean {
return Number.isInteger(index) && index >= 0 && index <= content.length;
} Try / catch
try {
recorder.insertLeft(index, snippet);
} catch (e) {
if (e instanceof IndexOutOfBoundException) {
throw new SchematicsException(`Edit offset ${index} out of range [0, ${e.length}]`);
}
throw e;
} Prevention
- Recompute offsets from fresh content immediately before each edit
- Handle indexOf === -1 search misses explicitly
- Clamp index with Math.min(index, content.length) when appending at end
- Guard against negative offsets from substring arithmetic
When it happens
Trigger: recorder.insertLeft(index, ...) / insertRight / remove with index < 0 or index > content.original.length — typically when a computed offset comes from stale search results, an empty file, or a different file's content.
Common situations: Regex/string search returning -1 then used as an offset; editing files that got truncated or emptied; off-by-one on length; reusing offsets computed against an older file version (version-change drift).
Related errors
- Failed to decode "${path}" as ${encoding} text.
- Content at path "${this.path}" has changed between the start
- 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/388a9b5bef437da0.
Report an issue: GitHub.