{"record":{"id":"9a1fb34ede2bd33c","repo":"can1357/oh-my-pi","slug":"conflict-entry-id-no-longer-present-in-entr","errorCode":null,"errorMessage":"Conflict #${entry.id} no longer present in '${entry.displayPath}': the recorded marker block can't be located. The file changed since the conflict was registered — re-read it to re-register conflicts.","messagePattern":"Conflict #(.+?) no longer present in '(.+?)': the recorded marker block can't be located\\. The file changed since the conflict was registered — re-read it to re-register conflicts\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/conflict-detect.ts","lineNumber":354,"sourceCode":" * block by content (anchored to `entry.startLine` as the preferred\n * match), so out-of-band edits earlier in the file that shift line\n * numbers don't break resolution. Throws clearly when the marker block\n * has actually been altered or removed.\n *\n * Boundary-echo repair (same philosophy as the edit tool's hashline\n * keeper repair): models frequently paste the \"whole resolved function\"\n * including the lines that live directly before/after the marker block,\n * which the verbatim splice would duplicate. Replacement lines that\n * exactly echo the adjacent context are dropped when the echo is\n * unambiguous — two or more consecutive lines, or a single line whose\n * removal fixes a delimiter-balance mismatch against the recorded sides.\n */\nexport function spliceConflict(originalText: string, entry: ConflictEntry, replacement: string): ConflictSplice {\n\tconst lines = originalText.split(\"\\n\");\n\tconst expected = buildRecordedRegion(entry);\n\tconst match = locateRegion(lines, expected, entry.startLine - 1);\n\tif (!match) {\n\t\tthrow new ToolError(\n\t\t\t`Conflict #${entry.id} no longer present in '${entry.displayPath}': the recorded marker block can't be located. The file changed since the conflict was registered — re-read it to re-register conflicts.`,\n\t\t);\n\t}\n\n\tconst trimmed = normalizeTrailingNewline(replacement);\n\tlet replacementLines = trimmed.split(\"\\n\").map(stripTrailingCr);\n\tconst echo = trimBoundaryEcho(replacementLines, lines, match, entry);\n\treplacementLines = echo.lines;\n\t// Round-trip fidelity for CRLF files: recorded sections are LF-normalized,\n\t// so re-apply \\r to spliced lines when the matched region used CRLF. The\n\t// final replacement line only carries \\r when another line follows it.\n\tif (lines[match.startIdx]!.endsWith(\"\\r\")) {\n\t\tconst hasFollowingLine = match.endIdx + 1 < lines.length;\n\t\treplacementLines = replacementLines.map((l, i) =>\n\t\t\ti < replacementLines.length - 1 || hasFollowingLine ? `${l}\\r` : l,\n\t\t);\n\t}\n\tconst next = [...lines.slice(0, match.startIdx), ...replacementLines, ...lines.slice(match.endIdx + 1)];","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/conflict-detect.ts#L336-L372","documentation":"spliceConflict locates the conflict marker block recorded when the conflict was registered (anchored to the recorded start line, matched by content). If the marker block — the <<<<<<< / ======= / >>>>>>> lines and the recorded side content — can no longer be found in the file's current text, resolution cannot proceed safely, so it throws. Line-number shifts from earlier edits are tolerated; actual content changes to the block are not.","triggerScenarios":"The file was edited (manually, by another tool, by a linter/formatter) after the conflict was registered and before the conflict:// write; the conflict markers were partially resolved or deleted; a formatter reflowed the conflict block's contents.","commonSituations":"Formatters (prettier, gofmt) rewriting the file between read and resolve; the agent editing the file with the normal edit tool in between; a user fixing conflicts by hand in an editor while an agent works on them; stale conflict registrations across sessions or rebases.","solutions":["Re-read the file so conflicts are re-registered against the current content, then resolve using the new ids.","Check the file's current state (git diff / open it) to see what changed since registration before retrying.","If a formatter is interfering, disable it for that file or format before reading, then resolve.","Re-run resolution in one step: read → write conflict://<new-id> without intermediate edits."],"exampleFix":"// before\n// conflict #3 registered earlier; file edited since\nwrite({ path: 'conflict://3', content: '@theirs' }); // throws\n// after\nconst reread = await read({ path: file }); // re-registers conflicts\nconst newId = reread.conflicts[0].id;\nwrite({ path: `conflict://${newId}`, content: '@theirs' });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await write({ path: `conflict://${entry.id}`, content });\n} catch (err) {\n  if (String(err?.message).includes('no longer present')) {\n    const reread = await read({ path: entry.displayPath }); // re-register conflicts\n    const fresh = reread.conflicts.find(c => c.id);\n    await write({ path: `conflict://${fresh.id}`, content });\n  } else throw err;\n}","preventionTips":["Never interleave ordinary edits or formatters between the registering read and the conflict write.","Resolve conflicts immediately after reading them.","Exclude conflicted files from format-on-save during resolution.","Re-read after any external change (rebase, manual fix) to refresh conflict ids."],"tags":["conflict-resolution","stale-state","merge-conflict","race-condition"],"backgroundTag":"stale-conflict-marker","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}