can1357/oh-my-pi · error
${trimmed} is valid only in REWRITE, never MATCH.
Error message
${trimmed} is valid only in REWRITE, never MATCH. What it means
»N (a numbered reference to an earlier operation's rewrite, the "register reference") is only meaningful inside a REWRITE block where it pulls in a previous operation's replacement text. When the parser encounters a »N line while still in the MATCH state with an empty pattern — i.e. before any MATCH content exists — it throws this error. A »N after non-empty MATCH content is tolerated as a mistyped » separator (a known model idiolect) and treated as the rewrite boundary instead.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:1358
throw new Error(`Expected ${OPENER} on input line ${index + 1}.`);
}
continue;
}
if (state === "pattern") {
const accumulated = patternLines.join("\n");
const markersBalanced = (accumulated.match(/⟪/gu) || []).length === (accumulated.match(/⟫/gu) || []).length;
if (trimmed === REWRITE_HEADER) {
state = "rewrite";
} else if (trimmed === "***" && markersBalanced && patternLines.some(entry => entry.trim() !== "")) {
// A bare *** after MATCH content is an apply-patch-habit separator.
state = "rewrite";
} else if (trimmed === SELECT_CLOSE && markersBalanced && patternLines.some(entry => entry.trim() !== "")) {
// A lone ⟫ after balanced selections is a mistyped » separator.
state = "rewrite";
} else if (registerReference && markersBalanced) {
if (!patternLines.some(entry => entry.trim() !== "")) {
throw new Error(`${trimmed} is valid only in REWRITE, never MATCH.`);
}
// A lone »N after MATCH is always a mistyped » separator (observed
// model idiolect); a genuine re-emit is authored as » then »N.
state = "rewrite";
referenceSeparator = trimmed;
} else if (parsedOpener !== false) {
// A doubled opener produced an empty operation; drop it as noise.
if (patternLines.some(entry => entry.trim() !== "")) finishPattern(index);
allMatches = parsedOpener === 0;
patternLines = [];
rewriteLines = [];
referenceSeparator = undefined;
} else {
patternLines.push(line);
}
continue;
}
View on GitHub (pinned to 9690622007)
Solutions
- Add the MATCH pattern under « before the »N line — every operation needs its own match text.
- If you meant to separate MATCH from REWRITE, use a bare » line instead of »N.
- If the intent was to reuse an earlier replacement, write the pattern, then », then »N inside the rewrite text.
Example fix
// before « »1 // after « pattern text » »1
Defensive patterns
Strategy: validation
Validate before calling
// »N must appear only after non-empty pattern content
let state = "outside", hasPattern = false;
for (const l of body.split("\n")) {
const t = l.trim();
if (/^»[1-9]\d*$/.test(t) && state === "pattern" && !hasPattern) throw new Error("»N needs a MATCH pattern first");
if (t === "«" || t === "«*") { state = "pattern"; hasPattern = false; }
else if (state === "pattern" && t !== "") hasPattern = true;
} Type guard
const isRegisterReference = (line: string): boolean => /^»[1-9]\d*$/.test(line.trim());
Try / catch
catch (err) {
if (err instanceof Error && err.message.includes("valid only in REWRITE, never MATCH")) {
// inject the missing pattern lines before the »N line and retry
}
} Prevention
- Every operation must restate its MATCH text even when reusing a prior rewrite via »N.
- Emit register references inside the rewrite section only: pattern, », then »N.
- When templating chained edits, always include the pattern block.
When it happens
Trigger: A payload containing »1/»2/... as the first line of an operation — directly after the « opener with no pattern lines — e.g. a model trying to reference a prior rewrite without restating the match.
Common situations: LLMs chaining operations via register references but forgetting the MATCH block; confusion between the »N reference syntax and the «» separator; compressed multi-edit payloads that skip restating context.
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}.
- Operation ${operations.length + 1} has a second ${REWRITE_HE
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0868237bbc6b3924.
Report an issue: GitHub.