can1357/oh-my-pi · error
${REWRITE_HEADER}${reference[1]} must reference an earlier o
Error message
${REWRITE_HEADER}${reference[1]} must reference an earlier operation, not self/forward. What it means
Inside a rewrite, a »N line reuses the replacement text of operation N — but only backwards: N must be strictly less than the current operation number (operations are 1-based, so line »2 inside operation 2 is self-reference and rejected). Self- and forward-references would be circular or undefined at apply time, so the parser rejects them up front during post-parse validation.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:1414
) {
// 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;
const rewrites = other.rewrite.kind === "explicit" ? [other.rewrite.text] : other.rewrite.replacements;
return rewrites.some(
rewrite =>
normalizeText(rewrite).text.includes(patternNormalized) ||
rewrite.split("\n").some(line => line.trim() === `${REWRITE_HEADER}${index + 1}`),
);
});
if (!justified) throw new Error(message);View on GitHub (pinned to 9690622007)
Solutions
- Change the reference to point at a strictly earlier operation (renumber if you reordered operations).
- Inline the desired text directly instead of referencing if no earlier operation holds it.
- Recount operations from 1 in payload order and fix all »N lines to satisfy N < current.
Example fix
// before (operation 3 referencing itself) « pattern » »3 // after « pattern » »1
Defensive patterns
Strategy: validation
Validate before calling
let op = 0;
for (const line of body.split("\n")) {
const t = line.trim();
if (t === "«" || t === "«*") op++;
const ref = t.match(/^»([1-9]\d*)$/);
if (ref && Number(ref[1]) >= op && op > 0) throw new Error(`»${ref[1]} is not an earlier operation (current: ${op})`);
} Type guard
const isBackwardReference = (n: number, currentOp: number): boolean => n > 0 && n < currentOp;
Try / catch
catch (err) {
if (err instanceof Error && err.message.includes("must reference an earlier operation")) {
// renumber »N references after any operation reordering
}
} Prevention
- After reordering or splicing operations, renumber all »N references.
- Reference numbering is 1-based payload order; reference only ops defined earlier.
- Prefer inlining text over »N chains when the chain is short.
When it happens
Trigger: A rewrite block (explicit text or a »-reference chain) containing a »N line where N >= the current operation's index+1 — e.g. operation 1 containing »1, or operation 3 containing »4.
Common situations: Models numbering references from 0 or miscounting operation order; copying a register line into the operation it defines; reordering operations after writing references without renumbering.
Related errors
- 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 ${
- Expected ${OPENER} on input line ${index + 1}.
- ${trimmed} is valid only in REWRITE, never MATCH.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/44aabc328f7ffac9.
Report an issue: GitHub.