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

  1. Rename one of the colliding files before selecting it
  2. Dedupe in the UI when files are added (skip or prompt on duplicate normalized names)
  3. 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

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


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/8f45bd900fa898fb. Report an issue: GitHub.