can1357/oh-my-pi · error
Operation ${operationNumber} has an unclosed selection marke
Error message
Operation ${operationNumber} has an unclosed selection marker ⟪; add closing ⟫. | Operation ${operationNumber} has an unmatched closing selection marker ⟫; add opening ⟪. What it means
validateSelectionMarkers counts ⟪ open and ⟫ close markers in an operation pattern and requires them to balance. Selection markers denote 'replace exactly this span'; an unbalanced count means the model either never closed a selection or closed one it never opened, so the replacement target is ambiguous.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:580
selected = true;
index += SELECT_OPEN.length;
} else if (pattern.startsWith(SELECT_CLOSE, index)) {
selected = false;
index += SELECT_CLOSE.length;
} else if (selected && pattern.startsWith(SELECT_DIVIDER, index)) {
return true;
} else {
index++;
}
}
return false;
}
function validateSelectionMarkers(pattern: string, operationNumber: number): void {
const openCount = (pattern.match(/⟪/gu) || []).length;
const closeCount = (pattern.match(/⟫/gu) || []).length;
if (openCount === closeCount) return;
throw new Error(
openCount > closeCount
? `Operation ${operationNumber} has an unclosed selection marker ⟪; add closing ⟫.`
: `Operation ${operationNumber} has an unmatched closing selection marker ⟫; add opening ⟪.`,
);
}
/**
* Resolve a selection whose content contains the divider character itself
* (box-drawing code). A trailing divider reads as a deletion of the content
* before it with the inner dividers literal; an odd count splits at the
* middle divider (a symmetric replacement keeps equal literals on each
* side); an even count reads as a deletion of the whole selected text. A
* wrong guess still fails loud downstream: the resolved old side must match
* the file.
*/
function resolveLiteralDividers(
selected: string,
operationNumber: number,View on GitHub (pinned to 9690622007)
Solutions
- Add the missing ⟫ after the selected text for every unclosed ⟪
- Delete the stray ⟫ or add the missing ⟪ before it
- Re-send the full corrected payload — ops apply atomically, so all operations must be re-submitted
Example fix
// before «old ⟪text»new // after «old ⟪text⟫»new
Defensive patterns
Strategy: validation
Validate before calling
const opens = (pattern.match(/⟪/gu) ?? []).length;
const closes = (pattern.match(/⟫/gu) ?? []).length;
if (opens !== closes) throw new Error(`unbalanced selection markers: ${opens} ⟪ vs ${closes} ⟫`); Try / catch
try {
applySloppy(content, input, ctx);
} catch (err) {
if (err instanceof Error && err.message.includes('selection marker')) {
// fix markers in payload and re-send full corrected payload
} else throw err;
} Prevention
- Count ⟪ and ⟫ are equal in every operation before submitting
- Never emit a bare ⟫ in pattern text unless closing an opened selection
- Regenerate the whole payload rather than hand-patching markers
When it happens
Trigger: Calling applySloppy with an operation whose pattern contains an odd/extra ⟪ without ⟫ (unclosed) or an ⟫ without ⟪ (unmatched closing).
Common situations: An LLM truncates output mid-selection; a stray ⟫ character in code being quoted; copy/paste dropping the closing marker line.
Related errors
- Operation ${operationNumber} has an unmatched closing select
- Operation ${operationNumber} has nested selection markers; u
- Operation ${operationNumber} mixes inline and bare selection
- ${REWRITE_HEADER}${reference[1]} is valid only inside an inl
- ${REWRITE_HEADER}${reference[1]} is valid only in REWRITE, n
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/02c2859a35e565d7.
Report an issue: GitHub.