can1357/oh-my-pi · error · Error
Operations ${previous.operationNumber} and ${current.operati
Error message
Operations ${previous.operationNumber} and ${current.operationNumber} target overlapping original spans near lines ${firstLine} and ${secondLine}.
Conflicting candidates:
Operation ${previous.operationNumber} near line ${firstLine}:
${operationPayload(operations[previous.operationNumber - 1])}
Operation ${current.operationNumber} near line ${secondLine}:
${operationPayload(operations[current.operationNumber - 1])}
Keep whichever states the intended final text and drop the other. What it means
After planning edits, the library verifies that operations target disjoint original spans. If candidates from two different operations overlap, applying both would conflict; overlapping candidates from the same operation are ignored as fuzzy-matcher artifacts, but across operations it throws. The message shows both operation payloads with the line numbers they target and advises keeping whichever states the intended final text.
Source
Thrown at packages/coding-agent/src/edit/sloppy.ts:3839
continue;
}
// Overlapping spans are only a conflict when they disagree. A broad
// `«*` plus a narrower op over one of its matches (rename + the line
// that contains it) produces byte-identical text for the shared
// region — merge instead of rejecting a payload that is consistent.
const merged = reconcileOverlap(content, previous, current);
if (merged) {
ordered[ordered.length - 1] = merged;
continue;
}
if (previous.operationNumber === current.operationNumber) {
// Overlapping candidates of the SAME operation are a fuzzy-matcher
// artifact (a pattern re-matching inside its own span); keep the first.
continue;
}
const firstLine = lineNumberAt(content, previous.start);
const secondLine = lineNumberAt(content, current.start);
throw new Error(
[
`Operations ${previous.operationNumber} and ${current.operationNumber} target overlapping original spans near lines ${firstLine} and ${secondLine}.`,
"Conflicting candidates:",
`Operation ${previous.operationNumber} near line ${firstLine}:\n${operationPayload(operations[previous.operationNumber - 1])}`,
`Operation ${current.operationNumber} near line ${secondLine}:\n${operationPayload(operations[current.operationNumber - 1])}`,
"Keep whichever states the intended final text and drop the other.",
].join("\n\n"),
);
}
let result = content;
for (const edit of ordered.reverse()) {
result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);
}
if (result === content) throwNoOp(undefined, { content, offset: lastMatchOffset });
noOpByPath.delete(context.path);
context.notes?.push(...recoveryNotes, ...deletionNotes.values());
return result;
}View on GitHub (pinned to 9690622007)
Solutions
- Delete one of the two conflicting operations, keeping the one whose REWRITE states the intended final text.
- Narrow one operation's PATTERN so its span no longer intersects the other's.
- Merge the two edits into a single operation covering the shared region.
Example fix
// before (two ops both cover lines 10-12) » 1 ... spans 10-12 ... » 2 ... spans 11-13 ... // after (single op) » 1 ... spans 10-12, REWRITE contains final text for all ...
Defensive patterns
Strategy: validation
Validate before calling
function assertDisjointSpans(planned: { start: number; end: number; n: number }[]) {
const sorted = [...planned].sort((a, b) => a.start - b.start);
for (let i = 1; i < sorted.length; i++) {
if (sorted[i].start < sorted[i - 1].end && sorted[i].n !== sorted[i - 1].n)
throw new Error(`Operations ${sorted[i-1].n} and ${sorted[i].n} overlap`);
}
} Type guard
const overlaps = (a: { start: number; end: number }, b: { start: number; end: number }): boolean =>
a.start < b.end && b.start < a.end; Try / catch
try {
applySloppyEdit(payload);
} catch (err) {
if (err instanceof Error && err.message.includes("target overlapping original spans")) {
payload = dropConflictingOperation(payload, err.message);
applySloppyEdit(payload);
} else throw err;
} Prevention
- Plan edits against disjoint file regions; merge edits that touch the same block
- Avoid broad patterns that swallow regions other operations also target
- Review line numbers in the error's 'Conflicting candidates' section before re-submitting
When it happens
Trigger: applySloppyEdit where candidate spans [previous.start, previous.end) and [current.start, current.end) intersect for different operationNumbers, detected while scanning ordered candidates (sloppy.ts:3839).
Common situations: Two edits rewriting overlapping regions (e.g. one replaces a block, another tweaks a line inside it); a broad PATTERN accidentally matching a region a later op also edits; payload assembled by concatenating edits to the same area.
Related errors
- Managed skill "${name}" already exists. Use action "update"
- Operation ${operationNumber} has selection markers in REWRIT
- Operation ${operationNumber} REWRITE has a whole-line ${GAP}
- ${REWRITE_HEADER}${reference[1]} must reference an earlier d
- ${base}${grounding}${hint ? `\n${hint}` : ""} (no-op: e.g. "
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d04b34137bbbb7d4.
Report an issue: GitHub.