schollz/croc · error · Error

Invalid file size for ${file.n ?? "unnamed file"}

Error message

Invalid file size for ${file.n ?? "unnamed file"}

What it means

finiteSize() validates each offered file's size field: it defaults missing sizes to 0, then requires Number.isSafeInteger and >= 0. Floats, negatives, NaN, Infinity, or integers beyond 2^53-1 fail, because such sizes cannot be tracked or summed reliably in JavaScript.

Source

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

    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"}`);
  }
  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}`);

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Send sizes as non-negative integers within the safe range in the offer.
  2. As a receiver, refuse transfers with malformed size metadata — do not clamp or default unknown sizes.
  3. In tests, include 's' (or accept the 0 default) for every file fixture.

Example fix

// before (fixture)
{ n: "a.bin", s: 1e19 }
// after
{ n: "a.bin", s: 1024 }
Defensive patterns

Strategy: type-guard

Validate before calling

function isSafeFileSize(size: number | undefined): boolean {
  const s = size ?? 0;
  return Number.isSafeInteger(s) && s >= 0;
}

Type guard

function isWireFileSize(file: { s?: number }): file is { s?: number } & { s: number } {
  const s = file.s ?? 0;
  return Number.isSafeInteger(s) && s >= 0;
}

Try / catch

try {
  const offer = validateSenderInfo(info);
} catch (error) {
  if (error instanceof Error && error.message.startsWith("Invalid file size")) {
    rejectOffer("malformed size metadata");
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: validateSenderInfo() processes a WireFileInfo whose 's' is fractional (0.5), negative, NaN/Infinity (e.g. parsed from 'NaN' in JSON), or larger than Number.MAX_SAFE_INTEGER (~9 PB).

Common situations: Hostile peer declaring a negative or astronomical size to break progress math; a peer encoding sizes as floats; hand-built test offers forgetting 's'; JSON containing exponent-notation sizes that parse to Infinity.

Related errors


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