can1357/oh-my-pi · error
Operation ${operationNumber} mixes inline and bare selection
Error message
Operation ${operationNumber} mixes inline and bare selections. Use ⟪old|new⟫ for every selection, or use a » rewrite for all bare selections. What it means
Within one operation, selections must use a single style: either every selection is inline '⟪old│new⟫' (sawInline) or every selection is bare '⟪…⟫' whose replacement comes from a » rewrite block (sawBare). Mixing both styles in one operation is ambiguous and rejected.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:683
sawBare = true;
patternText += pattern.slice(index, close + SELECT_CLOSE.length);
} else {
sawInline = true;
if (selected.indexOf(SELECT_DIVIDER, divider + SELECT_DIVIDER.length) === -1) {
patternText += `${SELECT_OPEN}${selected.slice(0, divider)}${SELECT_CLOSE}`;
replacements.push(selected.slice(divider + SELECT_DIVIDER.length));
} else {
const resolved = resolveLiteralDividers(selected, operationNumber);
patternText += `${SELECT_OPEN}${resolved.old}${SELECT_CLOSE}`;
replacements.push(resolved.desired);
notes.push(resolved.note);
}
}
index = close + SELECT_CLOSE.length;
}
if (sawBare && sawInline) {
throw new Error(
`Operation ${operationNumber} mixes inline and bare selections. Use ${SELECT_OPEN}old${SELECT_DIVIDER}new${SELECT_CLOSE} for every selection, or use a ${REWRITE_HEADER} rewrite for all bare selections.`,
);
}
return { patternText, replacements, notes };
}
/**
* Embed selection-only lines into the anchor line above them. Models write
* `anchorLine` then `⟪old│new⟫` on the next line to mean "change old inside
* that anchor"; literally the selection would match the next occurrence
* below instead. Relocates only when old occurs exactly once in the anchor.
*/
function relocateSelectionLines(patternText: string): string {
const lines = patternText.split("\n");
for (let index = 1; index < lines.length; index++) {
const previous = lines[index - 1];
const only = lines[index].trim().match(/^⟪([^⟪⟫]*)⟫$/u);
if (!only || previous.includes(SELECT_OPEN) || previous.includes(GAP) || previous.trim() === "") continue;View on GitHub (pinned to 9690622007)
Solutions
- Convert all selections to inline ⟪old│new⟫ form within the operation
- Or move all replacements into a » rewrite block and make every selection bare
- Restore the missing │ divider if one selection was meant to be inline
Example fix
// before «x ⟪a│b⟫ ⟪c⟫»y // after «x ⟪a│b⟫ ⟪c│y⟫»
Defensive patterns
Strategy: validation
Validate before calling
const selections = pattern.match(/⟪[^⟫]*⟫/gu) ?? [];
const hasInline = selections.some(s => s.includes('│'));
const hasBare = selections.some(s => !s.includes('│'));
if (hasInline && hasBare) throw new Error('mixed inline and bare selections in one operation'); Try / catch
try {
applySloppy(content, input, ctx);
} catch (err) {
if (err instanceof Error && err.message.includes('mixes inline and bare selections')) {
// normalize all selections to one style and re-send
} else throw err;
} Prevention
- Pick one selection style per operation: all inline ⟪old│new⟫ or all bare with a » rewrite
- Check that every intended inline selection contains the │ divider
- Keep edits with different styles in separate operations
When it happens
Trigger: An operation contains at least one ⟪old│new⟫ inline selection and at least one bare ⟪text⟫ selection (no │ divider) in the same pattern.
Common situations: An LLM merges two edits into one operation, using inline for one change and bare+rewrite for another; a │ divider is accidentally dropped from one selection, turning it bare and tripping the mix check.
Related errors
- Operation ${operationNumber} has an unclosed selection marke
- Operation ${operationNumber} has an unmatched closing select
- Operation ${operationNumber} has nested selection markers; u
- lines.join("\n")
- ${REWRITE_HEADER}${reference[1]} is valid only inside an inl
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/dbd479080ffd6ca2.
Report an issue: GitHub.