schollz/croc · warning · Error
Duplicate filename: ${outgoingName}
Error message
Duplicate filename: ${outgoingName} What it means
prepareFiles builds a Set of outgoing filenames (after normalizeOutgoingFileName) and rejects any duplicate before hashing starts. Duplicate names would collide on the recipient's filesystem, so they are refused up front. Normalization means names that differ in raw form can still collide (e.g. differing path separators mapping to the same basename).
Source
Thrown at web/src/protocol/client.ts:276
m: errorMessage(error).slice(0, 500),
}, key);
} catch {
// The connection may already be gone.
}
}
export async function prepareFiles(
selected: File[],
callbacks: TransferCallbacks = {},
signal?: AbortSignal,
) {
if (selected.length === 0) throw new Error("Choose at least one file");
const names = new Set<string>();
const outgoingNames = selected.map((file) => normalizeOutgoingFileName(file.name));
for (let index = 0; index < selected.length; index += 1) {
const file = selected[index];
const outgoingName = outgoingNames[index];
if (names.has(outgoingName)) throw new Error(`Duplicate filename: ${outgoingName}`);
if (!Number.isSafeInteger(file.size)) throw new Error(`File is too large: ${file.name}`);
names.add(outgoingName);
}
const prepared: PreparedFile[] = [];
const engine = wasm();
for (let index = 0; index < selected.length; index += 1) {
checkAbort(signal);
const file = selected[index];
callbacks.onStatus?.(`Hashing ${index + 1}/${selected.length}: ${file.name}`);
const hashHandle = await engine.hashInit();
const reader = file.stream().getReader();
try {
for (;;) {
checkAbort(signal);
const { done, value } = await reader.read();
if (done) break;
await engine.hashUpdate(hashHandle, value);View on GitHub (pinned to e25f1bdc04)
Solutions
- Rename one of the colliding files before selecting it
- Dedupe in the UI when files are added (skip or prompt on duplicate normalized names)
- Mirror normalizeOutgoingFileName in the picker check so the preview matches what prepareFiles enforces
Example fix
// before
const selected = [...folderAFiles, ...folderBFiles]; // both contain 'report.pdf'
await prepareFiles(selected);
// after: dedupe on add
function addFiles(current, incoming) {
const seen = new Set(current.map((f) => normalizeOutgoingFileName(f.name)));
return [...current, ...incoming.filter((f) => {
const name = normalizeOutgoingFileName(f.name);
if (seen.has(name)) return false;
seen.add(name);
return true;
})];
} Defensive patterns
Strategy: validation
Validate before calling
import { normalizeOutgoingFileName } from "../protocol/metadata";
function hasDuplicateNames(files) {
const seen = new Set();
for (const f of files) {
const name = normalizeOutgoingFileName(f.name);
if (seen.has(name)) return name;
seen.add(name);
}
return null;
}
const dup = hasDuplicateNames(selected);
if (dup) throw new Error(`Duplicate filename: ${dup}`); Try / catch
try { await prepareFiles(selected); } catch (e) {
if (/^Duplicate filename:/.test(e.message)) { markDuplicateInUI(e.message); return; }
throw e;
} Prevention
- Dedupe on add in the file picker UI using the same normalization
- Warn users dragging whole trees that basenames must be unique
- Re-check right before submit; selections can change between screens
When it happens
Trigger: Selecting two files with the same basename from different folders; two files whose normalized names (path stripping, separator unification) converge; re-adding the same file twice in the picker.
Common situations: Users dragging a whole tree where 'img/logo.png' appears in two subfolders; UI allowing repeated additions without dedupe.
Related errors
- Choose at least one file
- Code must be at least 6 characters
- Custom codes must use printable ASCII characters
- Received a duplicate file chunk
- Received a file chunk outside the advertised file size
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/8f45bd900fa898fb.
Report an issue: GitHub.