can1357/oh-my-pi · error · Error
MV destination is the same as ${target.path}.
Error message
MV destination is the same as ${target.path}. What it means
HashlinePatcher.prepare validates the target file before applying a patch. When the operation is a move (fileOp.kind === 'move') and the canonicalized destination equals the canonicalized source path, it throws, since moving a file onto itself is a no-op that would corrupt the patch pipeline's assumptions.
Source
Thrown at packages/hashline/src/patcher.ts:389
pathRecoveredFromTagMessage(target.path, recovered.section.path, target.fileHash as string),
);
target = recovered.section;
canonicalPath = recovered.canonicalPath;
read = await this.#tryRead(target.path);
}
}
// Gate the final (possibly recovered) target before any write work, so
// an unrecoverable read-only target (e.g. a plan-mode working-tree path)
// fails with the write guard rather than a misleading "file not found".
await this.fs.preflightWrite(target.path, { fileOp });
if (!read.exists) {
throw new Error(`File not found: ${target.path}. Use the write tool to create new files.`);
}
if (fileOp?.kind === "move" && this.fs.canonicalPath(fileOp.dest) === canonicalPath) {
throw new Error(`MV destination is the same as ${target.path}.`);
}
const { bom: bomFromText, text } = stripBom(read.rawContent);
const bom = bomFromText || (await this.#readBinaryBom(target.path));
const lineEnding = detectLineEnding(text);
const normalized = normalizeToLF(text);
const register = clipboard ?? {};
const applyResult =
fileOp?.kind === "rem"
? this.#applyWithRecovery({
section: target,
canonicalPath,
exists: read.exists,
normalized,
edits: [],
clipboard: register,
})View on GitHub (pinned to 9690622007)
Solutions
- Change the move destination so it differs from the source path
- Compare canonical paths before issuing the move and skip the no-op operation
- Resolve symlinks/relative segments in the dest before constructing the fileOp
- Split the operation: apply the patch first, then move to a genuinely different path
Example fix
// before
{ kind: "move", dest: "./src/a.ts" } // target already src/a.ts → throws
// after
if (canonicalPath(dest) !== canonicalPath(target.path)) {
applyOp({ kind: "move", dest });
} Defensive patterns
Strategy: validation
Validate before calling
if (fileOp?.kind === "move" &&
canonicalize(fileOp.dest) === canonicalize(target.path)) {
return; // no-op move, skip
} Try / catch
try {
await patcher.prepare(target, fileOp);
} catch (e) {
if (e.message.startsWith("MV destination is the same as")) {
// treat as no-op or fix the dest in the calling config
}
} Prevention
- Canonicalize both paths (resolve symlinks, . and ..) before issuing a move
- Skip move ops when src and dest resolve identically
- Warn users when a rename destination matches the source case-insensitively
- Generate move destinations programmatically rather than from raw template strings
When it happens
Trigger: Calling prepared/executeHashlineSingle/apply/preflight with a move fileOp whose dest normalizes (symlinks, relative segments, case) to the same path as target.path.
Common situations: Config or agent-generated mv step using './file' vs 'file' or a symlinked path pointing back at the source, template expansion producing identical src/dest, or a user renaming to the same name in a different case on a case-insensitive filesystem.
Related errors
- Line ${anchor.line} does not exist (file has ${fileLines.len
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
- Invalid pattern: {err}
- Failed to load tree-sitter language: {err}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/27c77ab3ad9ed528.
Report an issue: GitHub.