{"record":{"id":"d92b58d6e2fbc5ae","repo":"schollz/croc","slug":"invalid-file-size-for-file-n-unnamed-file","errorCode":null,"errorMessage":"Invalid file size for ${file.n ?? \"unnamed file\"}","messagePattern":"Invalid file size for (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/metadata.ts","lineNumber":58,"sourceCode":"    throw new Error(`Remote filename must be a basename: ${nameValue}`);\n  }\n  const name = nameSegments[0];\n  if (!name) throw new Error(\"Remote filename is empty\");\n  const path = folder === \".\" ? name : `${folder}/${name}`;\n  return { folder, name, path };\n}\n\nexport function normalizeOutgoingFileName(value: string) {\n  // Go's unicode.IsPrint accepts ASCII space but rejects the other Unicode\n  // separator characters commonly inserted into filenames by macOS.\n  const compatible = value.replace(/\\p{Z}+/gu, \" \");\n  return normalizeFilePath(\".\", compatible).name;\n}\n\nfunction finiteSize(file: WireFileInfo) {\n  const size = file.s ?? 0;\n  if (!Number.isSafeInteger(size) || size < 0) {\n    throw new Error(`Invalid file size for ${file.n ?? \"unnamed file\"}`);\n  }\n  return size;\n}\n\nexport function validateSenderInfo(info: SenderInfoWire): TransferOffer {\n  if (info.SendingText) throw new Error(\"Text transfers are not supported yet\");\n  if (info.HashAlgorithm && info.HashAlgorithm !== \"xxhash\") {\n    throw new Error(`Hash algorithm \"${info.HashAlgorithm}\" is not supported`);\n  }\n\n  const destinations = new Set<string>();\n  const files: OfferedFile[] = [];\n  let totalSize = 0;\n  for (const wire of info.FilesToTransfer ?? []) {\n    if (wire.sy) throw new Error(\"Symlink transfers are not supported in the browser\");\n    const normalized = normalizeFilePath(wire.fr ?? \".\", wire.n ?? \"\");\n    if (destinations.has(normalized.path)) {\n      throw new Error(`Duplicate destination path: ${normalized.path}`);","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/metadata.ts#L40-L76","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Send sizes as non-negative integers within the safe range in the offer.","As a receiver, refuse transfers with malformed size metadata — do not clamp or default unknown sizes.","In tests, include 's' (or accept the 0 default) for every file fixture."],"exampleFix":"// before (fixture)\n{ n: \"a.bin\", s: 1e19 }\n// after\n{ n: \"a.bin\", s: 1024 }","handlingStrategy":"type-guard","validationCode":"function isSafeFileSize(size: number | undefined): boolean {\n  const s = size ?? 0;\n  return Number.isSafeInteger(s) && s >= 0;\n}","typeGuard":"function isWireFileSize(file: { s?: number }): file is { s?: number } & { s: number } {\n  const s = file.s ?? 0;\n  return Number.isSafeInteger(s) && s >= 0;\n}","tryCatchPattern":"try {\n  const offer = validateSenderInfo(info);\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith(\"Invalid file size\")) {\n    rejectOffer(\"malformed size metadata\");\n    return;\n  }\n  throw error;\n}","preventionTips":["Emit sizes as non-negative safe integers in offers.","Reject NaN/Infinity/negative size metadata rather than defaulting it.","Cap accepted per-file sizes in receiving services so hostile metadata fails early with a clear message."],"tags":["validation","metadata","numeric-limits"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}