can1357/oh-my-pi · error
Expected ${OPENER} on input line ${index + 1}.
Error message
Expected ${OPENER} on input line ${index + 1}. What it means
The parser starts in the "outside" state: before the first « opener, the only legal non-empty input is the opener itself. Any other non-blank line before the first operation is rejected with this error, stating the 1-based input line number where the unexpected content was found. Blank lines are tolerated so leading whitespace does not break payloads.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:1340
}
if (
parsedOpener === false &&
(trimmed.startsWith(OPENER) ||
(trimmed.startsWith(REWRITE_HEADER) && trimmed !== REWRITE_HEADER && !registerReference))
) {
throw new Error(
`Invalid control line ${JSON.stringify(trimmed)}; use only ${OPENER}, ${OPENER}*, ${REWRITE_HEADER}, or ${REWRITE_HEADER}N in REWRITE.`,
);
}
if (state === "outside") {
if (parsedOpener !== false) {
allMatches = parsedOpener === 0;
patternLines = [];
rewriteLines = [];
referenceSeparator = undefined;
state = "pattern";
} else if (trimmed !== "") {
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.`);View on GitHub (pinned to 9690622007)
Solutions
- Start the payload with a « (or §path + «) opener line before any content.
- Delete any preamble text/prose before the first opener.
- If the content before the first opener is meaningful, move it inside the first operation's MATCH block.
Example fix
// before Here are the changes: « pattern » replacement // after « pattern » replacement
Defensive patterns
Strategy: validation
Validate before calling
const firstContent = body.split("\n").find(l => l.trim() !== "");
if (firstContent !== undefined && !(firstContent.trim() === "«" || firstContent.trim() === "«*" || /^§/.test(firstContent.trim()))) {
throw new Error("payload must start with a « opener");
} Type guard
const startsWithOpener = (body: string): boolean => {
const l = body.split("\n").find(x => x.trim() !== "");
return l !== undefined && (l.trim() === "«" || l.trim() === "«*");
}; Try / catch
catch (err) {
if (err instanceof Error && err.message.includes("Expected « on input line")) {
// drop leading prose/headers before the first « and retry
}
} Prevention
- Begin every payload directly with « — no explanations, headers, or diff markers before it.
- Trim prose from LLM output before passing the body to the applier.
- Use §path headers (which the section splitter consumes) for file targeting.
When it happens
Trigger: Passing a section body that starts with prose, a file path line, a diff header (--- a/...), or any other text before the first « opener line to the sloppy apply routine.
Common situations: Models prefixing payloads with explanations or «file» headers; tooling stripping the opener when slicing the body; pasted payloads that lost the first « line; bodies that include diff-style headers.
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 ${
- ${trimmed} is valid only in REWRITE, never MATCH.
- 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/4e5a065cc7be27a9.
Report an issue: GitHub.