{"record":{"id":"96b82bcd3e081564","repo":"schollz/croc","slug":"remote-filename-is-empty","errorCode":null,"errorMessage":"Remote filename is empty","messagePattern":"Remote filename is empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/metadata.ts","lineNumber":43,"sourceCode":"  if (/^(?:[a-zA-Z]:|\\/)/.test(value)) {\n    throw new Error(`Remote path must be relative: ${value}`);\n  }\n  const segments = cleanSegments(value);\n  const normalized = segments.join(\"/\") || \".\";\n  if (normalized.includes(\".ssh\")) {\n    throw new Error(`Remote path is not allowed: ${value}`);\n  }\n  return normalized;\n}\n\nexport function normalizeFilePath(folderValue: string, nameValue: string) {\n  const folder = normalizeFolder(folderValue);\n  const nameSegments = cleanSegments(nameValue);\n  if (nameSegments.length !== 1 || nameSegments[0] !== nameValue.replaceAll(\"\\\\\", \"/\")) {\n    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}","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/metadata.ts#L25-L61","documentation":"normalizeFilePath() throws when the single name segment is empty — i.e. the filename normalizes to nothing (empty string, '.', or a bare '/'). A file with no name cannot be saved, so the offer is rejected before any destination path is built.","triggerScenarios":"normalizeFilePath(folder, '') — typically an offered file whose wire 'n' field is missing (wire.n ?? '' fallback in validateSenderInfo) or is '.' or '/'. Also reachable via normalizeOutgoingFileName('') or ('.').","commonSituations":"Hostile/buggy peer omitting the name field; sender code building offers with an empty filename (e.g. path.basename returning '' for directory-like inputs); a directory entry mistakenly listed in FilesToTransfer.","solutions":["Ensure every file entry in the offer has a real basename in the 'n' field.","On the sender, derive names with path splitting that cannot return '' (guard against trailing slashes).","As a receiver, refuse offers that fail here — there is no sensible default filename to substitute safely."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasNonEmptyBasename(value: string): boolean {\n  const replaced = value.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n  return replaced !== \"\" && replaced !== \".\";\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { name } = normalizeFilePath(folder, candidateName);\n} catch (error) {\n  if (error instanceof Error && error.message === \"Remote filename is empty\") {\n    rejectOffer(\"file entry has no name\");\n    return;\n  }\n  throw error;\n}","preventionTips":["Require a non-empty 'n' field on every offered file before sending.","Guard path-splitting helpers so they never return '' for directory-like inputs.","Do not invent default filenames for unnamed remote entries — reject them."],"tags":["path-validation","metadata","validation"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}