{"record":{"id":"b3ec104e84289567","repo":"can1357/oh-my-pi","slug":"operation-operationnumber-rewrite-has-a-whole-l","errorCode":null,"errorMessage":"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.","messagePattern":"Operation (.+?) REWRITE has a whole-line (.+?) with no MATCH gap to re-emit\\. REWRITE is final text written verbatim: type the elided lines out, or add a matching (.+?) gap to MATCH\\. To write a literal (.+?) line, use the write tool\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":2642,"sourceCode":"\t\tthrow new Error(\n\t\t\t`Operation ${operationNumber} has selection markers in REWRITE; PATTERN is current text, REWRITE is final text.`,\n\t\t);\n\t}\n\tconst sentinels = selectedCaptureIndices.map((_, index) => `\\u0000V8GAP${index}\\u0000`);\n\tlet markerIndex = 0;\n\tlet marked = \"\";\n\tfor (let index = 0; index < rewrite.length; ) {\n\t\tconst gapMarker = rewrite.startsWith(GAP, index) ? GAP : undefined;\n\t\tif (gapMarker) {\n\t\t\tconst lineStart = rewrite.lastIndexOf(\"\\n\", index - 1) + 1;\n\t\t\tconst nextNewline = rewrite.indexOf(\"\\n\", index);\n\t\t\tconst lineEnd = nextNewline === -1 ? rewrite.length : nextNewline;\n\t\t\tconst line = rewrite.slice(lineStart, lineEnd);\n\t\t\tif (markerIndex >= sentinels.length) {\n\t\t\t\t// An unclaimed gap alone on its line is context elision, never final\n\t\t\t\t// text; writing it verbatim splices a literal `…` into the file.\n\t\t\t\tif (line.trim() === GAP) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`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.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tmarked += gapMarker;\n\t\t\t\tmarkerIndex++;\n\t\t\t} else {\n\t\t\t\tconst capture = captures[selectedCaptureIndices[markerIndex]] ?? \"\";\n\t\t\t\tconst openEnded = line.trim() === GAP || rewrite.slice(index + gapMarker.length, lineEnd).trim() === \"\";\n\t\t\t\tif (capture.includes(\"\\n\") && !openEnded) {\n\t\t\t\t\t// A mid-line gap with text after it on the same line cannot\n\t\t\t\t\t// re-emit a multi-line capture: it is literal final text (an\n\t\t\t\t\t// ellipsis inside a string), not a gap back-reference. The\n\t\t\t\t\t// capture stays available for later gaps.\n\t\t\t\t\tmarked += gapMarker;\n\t\t\t\t} else {\n\t\t\t\t\tmarked += sentinels[markerIndex];\n\t\t\t\t\tmarkerIndex++;\n\t\t\t\t}","sourceCodeStart":2624,"sourceCodeEnd":2660,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L2624-L2660","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Type out the elided lines in full in REWRITE; REWRITE is final text.","If the gap is meant to re-emit MATCH content, ensure the MATCH block has a corresponding gap and its order aligns.","If a literal '…' line is genuinely wanted in the file, use the write tool instead of the sloppy editor."],"exampleFix":"// before (REWRITE elides lines)\n====\n…\nnextLine\n\n// after (REWRITE spells lines out)\n====\nelidedLine1\nelidedLine2\nnextLine","handlingStrategy":"validation","validationCode":"const GAP = \"…\";\nfunction assertNoUnmatchedGapLines(patternGaps: number, rewriteText: string) {\n  const gapLines = rewriteText.split(\"\\n\").filter(l => l.trim() === GAP).length;\n  if (gapLines > patternGaps) throw new Error(\"REWRITE has more whole-line gaps than MATCH provides\");\n}","typeGuard":"const hasUnclaimedGapLine = (rewrite: string, gap: string): boolean =>\n  rewrite.split(\"\\n\").some(line => line.trim() === gap);","tryCatchPattern":"try {\n  applySloppyEdit(payload);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"whole-line\")) {\n    payload = expandGapLinesToLiteralText(payload, fileContent);\n    applySloppyEdit(payload);\n  } else throw err;\n}","preventionTips":["Treat REWRITE as verbatim final text: never elide unchanged lines with gap markers","Only use a gap line in REWRITE when the MATCH has a corresponding gap to re-emit","Use the write tool if a literal ellipsis line is truly intended"],"tags":["edit-dsl","gap-marker","rewrite-syntax"],"backgroundTag":"invalid-edit-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}