{"record":{"id":"ca35dcf721c08fbc","repo":"can1357/oh-my-pi","slug":"empty-patch-start-with-opener","errorCode":null,"errorMessage":"Empty patch. Start with ${OPENER}.","messagePattern":"Empty patch\\. Start with (.+?)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1406,"sourceCode":"\t\t\tif (nextContent !== undefined && parseOpener(nextContent) === false) {\n\t\t\t\tthrow new Error(`Operation ${operations.length + 1} has a second ${REWRITE_HEADER} line.`);\n\t\t\t}\n\t\t\t// A bare » before the next operation or at payload end is a stray\n\t\t\t// close-bracket: models sometimes wrap both MATCH and REWRITE in «…».\n\t\t} else if (\n\t\t\ttrimmed === SELECT_CLOSE &&\n\t\t\t(rewriteLines.join(\"\\n\").match(/⟪/gu) || []).length === (rewriteLines.join(\"\\n\").match(/⟫/gu) || []).length\n\t\t) {\n\t\t\t// A lone ⟫ with no open selection is a stray block terminator; REWRITE\n\t\t\t// is final text and never carries selection markers.\n\t\t} else {\n\t\t\trewriteLines.push(line);\n\t\t}\n\t}\n\n\tif (state === \"rewrite\") finish(lines.length);\n\telse if (state === \"pattern\") finishPattern(lines.length);\n\tif (operations.length === 0) throw new Error(`Empty patch. Start with ${OPENER}.`);\n\tfor (let index = 0; index < operations.length; index++) {\n\t\tconst operationRewrite = operations[index].rewrite;\n\t\tconst rewrites = operationRewrite.kind === \"explicit\" ? [operationRewrite.text] : operationRewrite.replacements;\n\t\tfor (const rewrite of rewrites) {\n\t\t\tfor (const line of rewrite.split(\"\\n\")) {\n\t\t\t\tconst reference = line.trim().match(/^»([1-9]\\d*)$/u);\n\t\t\t\tif (reference && Number(reference[1]) >= index + 1) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`${REWRITE_HEADER}${reference[1]} must reference an earlier operation, not self/forward.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor (const [index, message] of pendingSeparatorErrors) {\n\t\tconst patternNormalized = normalizeText(operations[index].patternText).text;\n\t\tconst justified = operations.some((other, otherIndex) => {\n\t\t\tif (otherIndex === index) return false;","sourceCodeStart":1388,"sourceCodeEnd":1424,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1388-L1424","documentation":"After scanning all lines, if zero operations were produced the payload is considered empty and this error is thrown. The only way to open an operation is a « line, so the message instructs the author to start the payload with it. This fires when the body contains only blank lines, only prose that was dropped, or only stray control lines that were tolerated-and-ignored.","triggerScenarios":"Calling apply/computeSloppySectionDiff with an empty section body, a body of whitespace-only lines, or a body whose every line was consumed as tolerated noise (e.g. only a glued «» line) without ever seeing a real « opener.","commonSituations":"Upstream section splitting produced an empty body; the model emitted only explanations with no actual edit block; a payload got truncated to nothing; wrong variable passed (empty string).","solutions":["Add at least one operation: a « line, pattern text, », and replacement.","Check that the section body passed to the parser actually contains the edit payload, not an empty slice.","Verify upstream splitting logic (splitSloppySections) matched the section correctly and didn't drop its body."],"exampleFix":"// before (empty body)\n\"\"\n\n// after\n\"«\nold function()\n»\nnew function()\"","handlingStrategy":"validation","validationCode":"if (sectionBody.trim() === \"\" || !/^«/m.test(sectionBody)) {\n  throw new Error(\"section body contains no operation (needs «)\");\n}","typeGuard":"const hasOperation = (body: string): boolean => body.split(\"\\n\").some(l => l.trim() === \"«\" || l.trim() === \"«*\");","tryCatchPattern":"try {\n  const result = await computeSloppySectionDiff(section, cwd);\n  if (\"error\" in result && result.error.includes(\"Empty patch\")) {\n    // skip or regenerate the section — there was nothing to apply\n  }\n} catch (err) { /* ... */ }","preventionTips":["Check the splitter/extractor actually captured body content for the section.","Reject empty or prose-only model responses before invoking the applier.","Log the raw payload when this fires to spot upstream truncation."],"tags":["parser","edit-format","empty-input"],"backgroundTag":"empty-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}