can1357/oh-my-pi · error · ToolError

Conflict #${entry.id} target '${entry.displayPath}' no longe

Error message

Conflict #${entry.id} target '${entry.displayPath}' no longer exists.

What it means

When resolving a previously registered read-conflict (a marker block surfaced by `read`), the tool re-checks that the conflict's target file still exists before splicing in the replacement. If the file was deleted (or moved/renamed) between the conflict registration and the resolve attempt, this ToolError aborts the resolution. Conflict entries hold a snapshot id but never resurrect missing files.

Source

Thrown at packages/coding-agent/src/tools/write.ts:854

	 * marker region in the registered file with `replacementContent`.
	 * The write deliberately bypasses the LSP writethrough: the file may
	 * still hold other unresolved marker blocks, so formatting could
	 * corrupt them and diagnostics would be marker-noise anyway.
	 *
	 * Entry ids are session-stable: they keep working even after later
	 * writes resolve other blocks in the same file. The recorded range
	 * is re-validated on disk before splicing so an out-of-band edit
	 * surfaces as a clear error instead of corrupting the file.
	 */
	async #resolveConflict(
		entry: ConflictEntry,
		replacementContent: string,
		stripped: boolean,
		signal: AbortSignal | undefined,
	): Promise<AgentToolResult<WriteToolDetails>> {
		const absolutePath = entry.absolutePath;
		if (!(await fs.exists(absolutePath))) {
			throw new ToolError(`Conflict #${entry.id} target '${entry.displayPath}' no longer exists.`);
		}

		const expanded = expandContentTokens(replacementContent, entry);
		const originalText = await Bun.file(absolutePath).text();
		const splice = spliceConflict(originalText, entry, expanded);
		const newContent = splice.text;

		await writethroughNoop(absolutePath, newContent, signal);
		invalidateFsScanAfterWrite(absolutePath);
		this.session.bumpFileMutationVersion?.(absolutePath);
		this.session.fileSnapshotStore?.invalidate(absolutePath);
		const history = this.session.conflictHistory;
		history?.invalidate(entry.id);
		if (history) {
			// Drop stale duplicate registrations of the same region: a re-read
			// after an out-of-band shift registers a fresh id at the new
			// startLine while the stale twin persists at the old one. A DISTINCT
			// conflict block that is merely byte-identical still occurs in the

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-read the file's directory to see its current state; recreate or restore the file if it should exist.
  2. If the file was intentionally deleted, abandon the conflict — no resolution is needed.
  3. If the file was renamed, re-read the new path so a fresh conflict/marker is registered, then resolve against that.
  4. Run `git status`/VCS inspection to understand what removed the file.
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from "node:fs/promises";
try { await fs.access(entry.absolutePath); } catch {
  throw new Error(`Conflict target ${entry.displayPath} is gone; re-read or abandon`);
}

Try / catch

try {
  await write(`conflict://${id}`, replacement);
} catch (err) {
  if (String(err.message).includes("no longer exists")) {
    // file was deleted/renamed — re-read or skip
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling write with conflict://<id> (or conflict://* bulk directives) after the target file at entry.absolutePath was deleted or renamed on disk.

Common situations: git operations (checkout/rebase) removing or renaming conflicted files, a cleanup step deleting drafts, the agent or user resolving the conflict by rewriting the file manually so the marker path vanished.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/cbc2cbc0f2171c19. Report an issue: GitHub.