can1357/oh-my-pi · error · ToolError
Conflict #${entry.id} no longer present in '${entry.displayP
Error message
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. What it means
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.
Source
Thrown at packages/coding-agent/src/tools/conflict-detect.ts:354
* block by content (anchored to `entry.startLine` as the preferred
* match), so out-of-band edits earlier in the file that shift line
* numbers don't break resolution. Throws clearly when the marker block
* has actually been altered or removed.
*
* Boundary-echo repair (same philosophy as the edit tool's hashline
* keeper repair): models frequently paste the "whole resolved function"
* including the lines that live directly before/after the marker block,
* which the verbatim splice would duplicate. Replacement lines that
* exactly echo the adjacent context are dropped when the echo is
* unambiguous — two or more consecutive lines, or a single line whose
* removal fixes a delimiter-balance mismatch against the recorded sides.
*/
export function spliceConflict(originalText: string, entry: ConflictEntry, replacement: string): ConflictSplice {
const lines = originalText.split("\n");
const expected = buildRecordedRegion(entry);
const match = locateRegion(lines, expected, entry.startLine - 1);
if (!match) {
throw new ToolError(
`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.`,
);
}
const trimmed = normalizeTrailingNewline(replacement);
let replacementLines = trimmed.split("\n").map(stripTrailingCr);
const echo = trimBoundaryEcho(replacementLines, lines, match, entry);
replacementLines = echo.lines;
// Round-trip fidelity for CRLF files: recorded sections are LF-normalized,
// so re-apply \r to spliced lines when the matched region used CRLF. The
// final replacement line only carries \r when another line follows it.
if (lines[match.startIdx]!.endsWith("\r")) {
const hasFollowingLine = match.endIdx + 1 < lines.length;
replacementLines = replacementLines.map((l, i) =>
i < replacementLines.length - 1 || hasFollowingLine ? `${l}\r` : l,
);
}
const next = [...lines.slice(0, match.startIdx), ...replacementLines, ...lines.slice(match.endIdx + 1)];View on GitHub (pinned to 9690622007)
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.
Example fix
// before
// conflict #3 registered earlier; file edited since
write({ path: 'conflict://3', content: '@theirs' }); // throws
// after
const reread = await read({ path: file }); // re-registers conflicts
const newId = reread.conflicts[0].id;
write({ path: `conflict://${newId}`, content: '@theirs' }); Defensive patterns
Strategy: try-catch
Try / catch
try {
await write({ path: `conflict://${entry.id}`, content });
} catch (err) {
if (String(err?.message).includes('no longer present')) {
const reread = await read({ path: entry.displayPath }); // re-register conflicts
const fresh = reread.conflicts.find(c => c.id);
await write({ path: `conflict://${fresh.id}`, content });
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Conflict #${entry.id} has no base section (2-way merge). `@b
- Conflict #${entry.id} has no base section (2-way merge). 'co
- Conflict #${entry.id} target '${entry.displayPath}' no longe
- Conflict #${id} not found. Conflict ids are registered when
- merge conflict in {} file(s)
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9a1fb34ede2bd33c.
Report an issue: GitHub.