schollz/croc · error · Error

Remote filename must be a basename: ${nameValue}

Error message

Remote filename must be a basename: ${nameValue}

What it means

normalizeFilePath() requires the file's name to be a single path segment: after backslash→slash replacement, cleanSegments(name) must yield exactly one segment equal to the replaced name. Any '/', multiple segments, or trailing slash (which would leave an empty final segment) fails this basename invariant.

Source

Thrown at web/src/protocol/metadata.ts:40

}

export function normalizeFolder(value = ".") {
  if (/^(?:[a-zA-Z]:|\/)/.test(value)) {
    throw new Error(`Remote path must be relative: ${value}`);
  }
  const segments = cleanSegments(value);
  const normalized = segments.join("/") || ".";
  if (normalized.includes(".ssh")) {
    throw new Error(`Remote path is not allowed: ${value}`);
  }
  return normalized;
}

export function normalizeFilePath(folderValue: string, nameValue: string) {
  const folder = normalizeFolder(folderValue);
  const nameSegments = cleanSegments(nameValue);
  if (nameSegments.length !== 1 || nameSegments[0] !== nameValue.replaceAll("\\", "/")) {
    throw new Error(`Remote filename must be a basename: ${nameValue}`);
  }
  const name = nameSegments[0];
  if (!name) throw new Error("Remote filename is empty");
  const path = folder === "." ? name : `${folder}/${name}`;
  return { folder, name, path };
}

export function normalizeOutgoingFileName(value: string) {
  // Go's unicode.IsPrint accepts ASCII space but rejects the other Unicode
  // separator characters commonly inserted into filenames by macOS.
  const compatible = value.replace(/\p{Z}+/gu, " ");
  return normalizeFilePath(".", compatible).name;
}

function finiteSize(file: WireFileInfo) {
  const size = file.s ?? 0;
  if (!Number.isSafeInteger(size) || size < 0) {
    throw new Error(`Invalid file size for ${file.n ?? "unnamed file"}`);

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Split the path yourself: put directories in the folder argument and the last segment in name — normalizeFilePath('docs', 'file.txt').
  2. Strip trailing slashes from names before offering.
  3. Reject offers from peers whose file names contain separators (they should use fr for folders).

Example fix

// before
normalizeFilePath(".", "docs/file.txt");
// after
normalizeFilePath("docs", "file.txt");
Defensive patterns

Strategy: validation

Validate before calling

function isBasename(value: string): boolean {
  const replaced = value.replaceAll("\\", "/");
  return replaced.length > 0 && !replaced.includes("/");
}
if (!isBasename(nameValue)) throw new TypeError(`not a basename: ${nameValue}`);

Try / catch

try {
  const { path } = normalizeFilePath(folder, name);
} catch (error) {
  if (error instanceof Error && error.message.startsWith("Remote filename must be a basename")) {
    // split folder/name yourself and retry, or reject the offer
    const idx = name.lastIndexOf("/");
    return normalizeFilePath(`${folder}/${name.slice(0, idx)}`, name.slice(idx + 1));
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling normalizeFilePath(folder, name) with name='docs/file.txt', name='a/', name='dir/' (trailing slash yields a segment list mismatch or empty last segment), or any name containing separators. Reached from validateSenderInfo for each offered file and from normalizeOutgoingFileName.

Common situations: Sender-side code passing a full relative path as the filename instead of splitting folder/name; a hostile peer smuggling path components in n; sending a directory-shaped entry as a file; names ending in '/' from sloppy path joins.

Related errors


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