{"record":{"id":"09c15cdc45e34813","repo":"can1357/oh-my-pi","slug":"message-pending-separator-error-justified-check","errorCode":null,"errorMessage":"message (pending separator error, justified-check failure)","messagePattern":"message \\(pending separator error, justified-check failure\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1432,"sourceCode":"\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;\n\t\t\tconst rewrites = other.rewrite.kind === \"explicit\" ? [other.rewrite.text] : other.rewrite.replacements;\n\t\t\treturn rewrites.some(\n\t\t\t\trewrite =>\n\t\t\t\t\tnormalizeText(rewrite).text.includes(patternNormalized) ||\n\t\t\t\t\trewrite.split(\"\\n\").some(line => line.trim() === `${REWRITE_HEADER}${index + 1}`),\n\t\t\t);\n\t\t});\n\t\tif (!justified) throw new Error(message);\n\t}\n\treturn operations;\n}\n\nfunction normalizeText(source: string): NormalizedText {\n\tlet text = \"\";\n\tconst starts: number[] = [];\n\tconst ends: number[] = [];\n\tfor (let index = 0; index < source.length; ) {\n\t\tconst codePoint = source.codePointAt(index);\n\t\tif (codePoint === undefined) break;\n\t\tif (codePoint <= 0x7f) {\n\t\t\tconst next = index + 1;\n\t\t\tif (!((codePoint >= 0x09 && codePoint <= 0x0d) || codePoint === 0x20)) {\n\t\t\t\ttext += source[index];\n\t\t\t\tstarts.push(index);\n\t\t\t\tends.push(next);\n\t\t\t}","sourceCodeStart":1414,"sourceCodeEnd":1450,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1414-L1450","documentation":"When a pattern-only block was optimistically interpreted as a deletion (assumedDeletion, error 1220's sibling path), the parser records the would-be \"needs separator\" error as pending. After parsing, it keeps the deletion only if justified: some later operation's rewrite re-emits the deleted text verbatim (normalized) or references this operation via »N — i.e. it looks like the delete-half of a move. If not justified, the saved error message is thrown as-is.","triggerScenarios":"A payload contains a multi-line pattern-only block (>= 24 normalized chars) with no «» separator, and no subsequent operation re-includes that text in its rewrite or issues »N pointing back at it — the parser cannot confirm the deletion was intentional.","commonSituations":"Models omitting «» for a true deletion of a long block (rejected as too risky to guess); intended moves where the re-emit didn't match exactly (whitespace/wording drift); deletions the author actually meant as rewrites.","solutions":["Add an explicit «» separator: if it's a rewrite, put the new text after »; if it's a true deletion, leave the rewrite empty — explicit intent is always accepted.","If this is part of a move, ensure the later operation's rewrite contains the deleted text (matching after normalization) or a »N reference to this operation.","Paste the corrected payload from the thrown message, filling in <new text>."],"exampleFix":"// before (ambiguous pattern-only block)\n«\nlong block of text\n«\nnew location text\n»\nlong block of text\n\n// after (explicit move)\n«\nlong block of text\n»\n«\nnew location text\n»\nlong block of text","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const result = await computeSloppySectionDiff(section, cwd);\n  if (\"error\" in result) {\n    // this error surfaces with the full copy-ready corrected payload;\n    // resubmit it with <new text> filled in (or keep the deletion explicit\n    // via an empty rewrite after »)\n  }\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"needs »\")) { /* handle */ }\n}","preventionTips":["Never rely on the assumed-deletion heuristic; always pair pattern-only blocks with an explicit empty rewrite (» with nothing after).","For moves, make the re-emit byte-identical after normalization or use a »N reference.","Treat any \"pending separator\" design as last-resort; generate explicit separators in your authoring pipeline."],"tags":["parser","edit-format","ambiguous-deletion"],"backgroundTag":"missing-section-separator","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}