earendil-works/pi · error

Replacement range is outside the base content.

Error message

Replacement range is outside the base content.

What it means

Internal consistency guard in getReplacementLineRange: a matched edit's character range (matchIndex..matchIndex+matchLength) does not fall inside any line span of the base content. This indicates the replacement was matched against different content than the line-indexed base, i.e. an alignment bug or mutated input rather than a user error.

Solutions

  1. Ensure the same baseContent string is used for matching and line-span computation
  2. Recompute matches after any normalization instead of reusing stale indices
  3. Report as an internal bug if it reproduces with a single straightforward edit
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at packages/coding-agent/src/core/tools/edit-diff.ts:97 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24). Data as JSON: /api/errors/8b1958caefc7da8f. Report an issue: GitHub.

Appendix: source

Thrown at packages/coding-agent/src/core/tools/edit-diff.ts:97

		offset = span.end;
		return span;
	});
}

function getReplacementLineRange(lines: LineSpan[], replacement: TextReplacement) {
	const replacementStart = replacement.matchIndex;
	const replacementEnd = replacement.matchIndex + replacement.matchLength;

	let startLine = -1;
	for (let i = 0; i < lines.length; i++) {
		const line = lines[i];
		if (replacementStart >= line.start && replacementStart < line.end) {
			startLine = i;
			break;
		}
	}
	if (startLine === -1) {
		throw new Error("Replacement range is outside the base content.");
	}

	let endLine = startLine;
	while (endLine < lines.length && lines[endLine].end < replacementEnd) {
		endLine++;
	}
	if (endLine >= lines.length) {
		throw new Error("Replacement range is outside the base content.");
	}

	return { startLine, endLine: endLine + 1 };
}

function applyReplacements(content: string, replacements: TextReplacement[], offset = 0): string {
	let result = content;
	for (let i = replacements.length - 1; i >= 0; i--) {
		const replacement = replacements[i];
		const matchIndex = replacement.matchIndex - offset;

View on GitHub (pinned to 4af9d21d3b)