schollz/croc · error · Error
Duplicate destination path: ${normalized.path}
Error message
Duplicate destination path: ${normalized.path} What it means
validateSenderInfo() keeps a Set of destination paths while ingesting offered files and throws when two files normalize to the same destination path. Duplicate destinations would make the second file silently overwrite the first in the download sink, so the offer is rejected.
Source
Thrown at web/src/protocol/metadata.ts:76
throw new Error(`Invalid file size for ${file.n ?? "unnamed file"}`);
}
return size;
}
export function validateSenderInfo(info: SenderInfoWire): TransferOffer {
if (info.SendingText) throw new Error("Text transfers are not supported yet");
if (info.HashAlgorithm && info.HashAlgorithm !== "xxhash") {
throw new Error(`Hash algorithm "${info.HashAlgorithm}" is not supported`);
}
const destinations = new Set<string>();
const files: OfferedFile[] = [];
let totalSize = 0;
for (const wire of info.FilesToTransfer ?? []) {
if (wire.sy) throw new Error("Symlink transfers are not supported in the browser");
const normalized = normalizeFilePath(wire.fr ?? ".", wire.n ?? "");
if (destinations.has(normalized.path)) {
throw new Error(`Duplicate destination path: ${normalized.path}`);
}
destinations.add(normalized.path);
const size = finiteSize(wire);
totalSize += size;
if (!Number.isSafeInteger(totalSize)) throw new Error("Transfer size is too large");
files.push({
...normalized,
size,
hash: wire.h ? base64ToBytes(wire.h) : new Uint8Array(),
modified: wire.m,
mode: wire.md,
});
}
const emptyFolders: string[] = [];
for (const wire of info.EmptyFoldersToTransfer ?? []) {
const folder = normalizeFolder(wire.fr ?? ".");
if (destinations.has(folder)) {View on GitHub (pinned to e25f1bdc04)
Solutions
- Fix the sender so each file has a unique (folder, basename) destination.
- On the receiver, refuse the duplicate offer — do not auto-rename, since the peer's intent is ambiguous.
- Watch for the sibling case this check cannot catch: names differing only by case are allowed here but can overwrite on case-insensitive filesystems; dedupe those yourself if targeting macOS/Windows.
Example fix
// before (offer)
[{ fr: "a/b", n: "c.txt" }, { fr: "a", n: "b/c.txt" }]
// after
[{ fr: "a/b", n: "c.txt" }, { fr: "a/b", n: "c-2.txt" }] Defensive patterns
Strategy: validation
Validate before calling
function hasUniqueDestinations(info: SenderInfoWire): boolean {
const seen = new Set<string>();
for (const f of info.FilesToTransfer ?? []) {
const key = `${f.fr ?? "."}/${f.n ?? ""}`.replaceAll("\\", "/");
if (seen.has(key)) return false;
seen.add(key);
}
return true;
} Try / catch
try {
const offer = validateSenderInfo(info);
} catch (error) {
if (error instanceof Error && error.message.startsWith("Duplicate destination path")) {
rejectOffer("ambiguous duplicate destinations");
return;
}
throw error;
} Prevention
- Give every offered file a unique (folder, basename) pair; construct paths with one join helper.
- Dedupe on the sender before offering.
- Additionally check case-insensitive collisions yourself when saving to macOS/Windows filesystems.
When it happens
Trigger: An offer containing two entries whose (folder, name) pairs normalize identically — e.g. folder 'a/b' + name 'c.txt' and folder 'a' + name 'b/c.txt' (both yield 'a/b/c.txt'), or literally repeated entries. Case-sensitive comparison: 'A.txt' and 'a.txt' collide only on case-insensitive filesystems at save time, not here.
Common situations: Hostile peer probing overwrite behavior; sender with sloppy path construction producing equivalent folder/name splits; hand-built offers in tests; case-collision duplicates ('F.txt' vs 'f.txt') that pass this check but overwrite each other on macOS/Windows at write time.
Related errors
- Duplicate destination path: ${folder}
- Remote filename is empty
- Invalid file size for ${file.n ?? "unnamed file"}
- Transfer size is too large
- Code must be at least 6 characters
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/a0ee2672d3effe22.
Report an issue: GitHub.