can1357/oh-my-pi · error · Error

Operation ${operationNumber} REWRITE has a whole-line ${GAP}

Error message

Operation ${operationNumber} REWRITE has a whole-line ${GAP} with no MATCH gap to re-emit. REWRITE is final text written verbatim: type the elided lines out, or add a matching ${GAP} gap to MATCH. To write a literal ${GAP} line, use the write tool.

What it means

In the sloppy DSL, a whole-line gap marker (…) in REWRITE normally re-emits a captured MATCH gap (the elided lines between pattern segments). When REWRITE contains more gap lines than the MATCH provided gaps to re-emit, the surplus gap is context elision that would otherwise be written into the file as a literal '…' line. The library refuses because REWRITE is final text written verbatim.

Source

Thrown at packages/coding-agent/src/edit/sloppy.ts:2642

		throw new Error(
			`Operation ${operationNumber} has selection markers in REWRITE; PATTERN is current text, REWRITE is final text.`,
		);
	}
	const sentinels = selectedCaptureIndices.map((_, index) => `\u0000V8GAP${index}\u0000`);
	let markerIndex = 0;
	let marked = "";
	for (let index = 0; index < rewrite.length; ) {
		const gapMarker = rewrite.startsWith(GAP, index) ? GAP : undefined;
		if (gapMarker) {
			const lineStart = rewrite.lastIndexOf("\n", index - 1) + 1;
			const nextNewline = rewrite.indexOf("\n", index);
			const lineEnd = nextNewline === -1 ? rewrite.length : nextNewline;
			const line = rewrite.slice(lineStart, lineEnd);
			if (markerIndex >= sentinels.length) {
				// An unclaimed gap alone on its line is context elision, never final
				// text; writing it verbatim splices a literal `…` into the file.
				if (line.trim() === GAP) {
					throw new Error(
						`Operation ${operationNumber} REWRITE has a whole-line ${GAP} with no MATCH gap to re-emit. REWRITE is final text written verbatim: type the elided lines out, or add a matching ${GAP} gap to MATCH. To write a literal ${GAP} line, use the write tool.`,
					);
				}
				marked += gapMarker;
				markerIndex++;
			} else {
				const capture = captures[selectedCaptureIndices[markerIndex]] ?? "";
				const openEnded = line.trim() === GAP || rewrite.slice(index + gapMarker.length, lineEnd).trim() === "";
				if (capture.includes("\n") && !openEnded) {
					// A mid-line gap with text after it on the same line cannot
					// re-emit a multi-line capture: it is literal final text (an
					// ellipsis inside a string), not a gap back-reference. The
					// capture stays available for later gaps.
					marked += gapMarker;
				} else {
					marked += sentinels[markerIndex];
					markerIndex++;
				}

View on GitHub (pinned to 9690622007)

Solutions

  1. Type out the elided lines in full in REWRITE; REWRITE is final text.
  2. If the gap is meant to re-emit MATCH content, ensure the MATCH block has a corresponding gap and its order aligns.
  3. If a literal '…' line is genuinely wanted in the file, use the write tool instead of the sloppy editor.

Example fix

// before (REWRITE elides lines)
====
…
nextLine

// after (REWRITE spells lines out)
====
elidedLine1
elidedLine2
nextLine
Defensive patterns

Strategy: validation

Validate before calling

const GAP = "…";
function assertNoUnmatchedGapLines(patternGaps: number, rewriteText: string) {
  const gapLines = rewriteText.split("\n").filter(l => l.trim() === GAP).length;
  if (gapLines > patternGaps) throw new Error("REWRITE has more whole-line gaps than MATCH provides");
}

Type guard

const hasUnclaimedGapLine = (rewrite: string, gap: string): boolean =>
  rewrite.split("\n").some(line => line.trim() === gap);

Try / catch

try {
  applySloppyEdit(payload);
} catch (err) {
  if (err instanceof Error && err.message.includes("whole-line")) {
    payload = expandGapLinesToLiteralText(payload, fileContent);
    applySloppyEdit(payload);
  } else throw err;
}

Prevention

When it happens

Trigger: applySloppyEdit with an operation whose REWRITE has a whole-line gap marker at a position where all MATCH gaps have already been consumed (markerIndex >= sentinels.length) and the line is exactly the gap marker (modulo whitespace).

Common situations: Author elides unchanged lines in REWRITE the same way they were elided in PATTERN, forgetting REWRITE must spell the final text out; model-generated edits that copy PATTERN's ellipsis style into REWRITE.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b3ec104e84289567. Report an issue: GitHub.