{"record":{"id":"d04b34137bbbb7d4","repo":"can1357/oh-my-pi","slug":"operations-previous-operationnumber-and-curre","errorCode":null,"errorMessage":"Operations ${previous.operationNumber} and ${current.operationNumber} target overlapping original spans near lines ${firstLine} and ${secondLine}.\nConflicting candidates:\nOperation ${previous.operationNumber} near line ${firstLine}:\n${operationPayload(operations[previous.operationNumber - 1])}\nOperation ${current.operationNumber} near line ${secondLine}:\n${operationPayload(operations[current.operationNumber - 1])}\nKeep whichever states the intended final text and drop the other.","messagePattern":"Operations (.+?) and (.+?) target overlapping original spans near lines (.+?) and (.+?)\\.\nConflicting candidates:\nOperation (.+?) near line (.+?):\n(.+?)\nOperation (.+?) near line (.+?):\n(.+?)\nKeep whichever states the intended final text and drop the other\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":3839,"sourceCode":"\t\t\tcontinue;\n\t\t}\n\t\t// Overlapping spans are only a conflict when they disagree. A broad\n\t\t// `«*` plus a narrower op over one of its matches (rename + the line\n\t\t// that contains it) produces byte-identical text for the shared\n\t\t// region — merge instead of rejecting a payload that is consistent.\n\t\tconst merged = reconcileOverlap(content, previous, current);\n\t\tif (merged) {\n\t\t\tordered[ordered.length - 1] = merged;\n\t\t\tcontinue;\n\t\t}\n\t\tif (previous.operationNumber === current.operationNumber) {\n\t\t\t// Overlapping candidates of the SAME operation are a fuzzy-matcher\n\t\t\t// artifact (a pattern re-matching inside its own span); keep the first.\n\t\t\tcontinue;\n\t\t}\n\t\tconst firstLine = lineNumberAt(content, previous.start);\n\t\tconst secondLine = lineNumberAt(content, current.start);\n\t\tthrow new Error(\n\t\t\t[\n\t\t\t\t`Operations ${previous.operationNumber} and ${current.operationNumber} target overlapping original spans near lines ${firstLine} and ${secondLine}.`,\n\t\t\t\t\"Conflicting candidates:\",\n\t\t\t\t`Operation ${previous.operationNumber} near line ${firstLine}:\\n${operationPayload(operations[previous.operationNumber - 1])}`,\n\t\t\t\t`Operation ${current.operationNumber} near line ${secondLine}:\\n${operationPayload(operations[current.operationNumber - 1])}`,\n\t\t\t\t\"Keep whichever states the intended final text and drop the other.\",\n\t\t\t].join(\"\\n\\n\"),\n\t\t);\n\t}\n\tlet result = content;\n\tfor (const edit of ordered.reverse()) {\n\t\tresult = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);\n\t}\n\tif (result === content) throwNoOp(undefined, { content, offset: lastMatchOffset });\n\tnoOpByPath.delete(context.path);\n\tcontext.notes?.push(...recoveryNotes, ...deletionNotes.values());\n\treturn result;\n}","sourceCodeStart":3821,"sourceCodeEnd":3857,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L3821-L3857","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before (two ops both cover lines 10-12)\n» 1  ... spans 10-12 ...\n» 2  ... spans 11-13 ...\n\n// after (single op)\n» 1  ... spans 10-12, REWRITE contains final text for all ...","handlingStrategy":"validation","validationCode":"function assertDisjointSpans(planned: { start: number; end: number; n: number }[]) {\n  const sorted = [...planned].sort((a, b) => a.start - b.start);\n  for (let i = 1; i < sorted.length; i++) {\n    if (sorted[i].start < sorted[i - 1].end && sorted[i].n !== sorted[i - 1].n)\n      throw new Error(`Operations ${sorted[i-1].n} and ${sorted[i].n} overlap`);\n  }\n}","typeGuard":"const overlaps = (a: { start: number; end: number }, b: { start: number; end: number }): boolean =>\n  a.start < b.end && b.start < a.end;","tryCatchPattern":"try {\n  applySloppyEdit(payload);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"target overlapping original spans\")) {\n    payload = dropConflictingOperation(payload, err.message);\n    applySloppyEdit(payload);\n  } else throw err;\n}","preventionTips":["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"],"tags":["edit-dsl","overlap","conflict"],"backgroundTag":"overlapping-edit-spans","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}