can1357/oh-my-pi · error
Empty patch. Start with ${OPENER}.
Error message
Empty patch. Start with ${OPENER}. What it means
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.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:1406
if (nextContent !== undefined && parseOpener(nextContent) === false) {
throw new Error(`Operation ${operations.length + 1} has a second ${REWRITE_HEADER} line.`);
}
// A bare » before the next operation or at payload end is a stray
// close-bracket: models sometimes wrap both MATCH and REWRITE in «…».
} else if (
trimmed === SELECT_CLOSE &&
(rewriteLines.join("\n").match(/⟪/gu) || []).length === (rewriteLines.join("\n").match(/⟫/gu) || []).length
) {
// A lone ⟫ with no open selection is a stray block terminator; REWRITE
// is final text and never carries selection markers.
} else {
rewriteLines.push(line);
}
}
if (state === "rewrite") finish(lines.length);
else if (state === "pattern") finishPattern(lines.length);
if (operations.length === 0) throw new Error(`Empty patch. Start with ${OPENER}.`);
for (let index = 0; index < operations.length; index++) {
const operationRewrite = operations[index].rewrite;
const rewrites = operationRewrite.kind === "explicit" ? [operationRewrite.text] : operationRewrite.replacements;
for (const rewrite of rewrites) {
for (const line of rewrite.split("\n")) {
const reference = line.trim().match(/^»([1-9]\d*)$/u);
if (reference && Number(reference[1]) >= index + 1) {
throw new Error(
`${REWRITE_HEADER}${reference[1]} must reference an earlier operation, not self/forward.`,
);
}
}
}
}
for (const [index, message] of pendingSeparatorErrors) {
const patternNormalized = normalizeText(operations[index].patternText).text;
const justified = operations.some((other, otherIndex) => {
if (otherIndex === index) return false;View on GitHub (pinned to 9690622007)
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.
Example fix
// before (empty body) "" // after "« old function() » new function()"
Defensive patterns
Strategy: validation
Validate before calling
if (sectionBody.trim() === "" || !/^«/m.test(sectionBody)) {
throw new Error("section body contains no operation (needs «)");
} Type guard
const hasOperation = (body: string): boolean => body.split("\n").some(l => l.trim() === "«" || l.trim() === "«*"); Try / catch
try {
const result = await computeSloppySectionDiff(section, cwd);
if ("error" in result && result.error.includes("Empty patch")) {
// skip or regenerate the section — there was nothing to apply
}
} catch (err) { /* ... */ } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- No files were modified.
- Diff does not contain any lines
- needsSeparator (Operation ${operations.length + 1} needs ${R
- ${trimmed} is not a valid opener. Use ${OPENER} with a patte
- Invalid control line ${JSON.stringify(trimmed)}; use only ${
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ca35dcf721c08fbc.
Report an issue: GitHub.