{"record":{"id":"534ff6da1dda3778","repo":"schollz/croc","slug":"stored-transfers-can-contain-at-most-settings-ma","errorCode":null,"errorMessage":"Stored transfers can contain at most ${settings.maxFiles} files","messagePattern":"Stored transfers can contain at most (.+?) files","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web/src/protocol/stored.ts","lineNumber":297,"sourceCode":"      const { done, value } = await reader.read();\n      if (done) break;\n      await engine.sha256Update(handle, value);\n    }\n    return await engine.sha256Final(handle);\n  } finally {\n    reader.releaseLock();\n  }\n}\n\nexport async function prepareStoredFiles(\n  selected: File[],\n  settings: StoredSettings,\n  callbacks: { onStatus?(status: string): void } = {},\n  signal?: AbortSignal,\n) {\n  if (selected.length === 0) throw new Error(\"Choose at least one file\");\n  if (selected.length > settings.maxFiles) {\n    throw new Error(`Stored transfers can contain at most ${settings.maxFiles} files`);\n  }\n  const total = selected.reduce((sum, file) => sum + file.size, 0);\n  if (!Number.isSafeInteger(total) || total > settings.maxTransferBytes) {\n    throw new Error(`Stored transfer exceeds the ${settings.maxTransferBytes} byte limit`);\n  }\n  const names = new Set<string>();\n  const prepared: StoredPreparedFile[] = [];\n  let firstChunk = 0;\n  for (let index = 0; index < selected.length; index += 1) {\n    const file = selected[index];\n    const name = normalizeOutgoingFileName(file.name);\n    if (names.has(name)) throw new Error(`Duplicate filename: ${name}`);\n    names.add(name);\n    callbacks.onStatus?.(`Hashing ${index + 1}/${selected.length}: ${name}`);\n    const chunkCount = Math.ceil(file.size / storedChunkSize);\n    prepared.push({\n      file,\n      name,","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/stored.ts#L279-L315","documentation":"prepareStoredFiles() enforces settings.maxFiles: a stored transfer may contain at most that many files. The limit exists because each file adds manifest entries and per-file hashing time, and the server applies the same cap on creation.","triggerScenarios":"selected.length > settings.maxFiles, e.g. selecting 51 files when maxFiles is 50, or dropping an entire directory tree with thousands of files onto the picker.","commonSituations":"Users zipping-less workflows that drop whole folders; operators who lower maxFiles server-side while clients still show the old limit; batch sends that exceed the configured cap.","solutions":["Split the send into multiple stored transfers within the limit","Pre-check at the UI layer and show the allowed count before submit","If you operate the service and legitimately need more files, raise maxFiles in the settings served to clients (server must accept it too)"],"exampleFix":"// before\nconst prepared = await prepareStoredFiles(selected, settings);\n\n// after\nif (selected.length > settings.maxFiles) { alert(`At most ${settings.maxFiles} files`); return; }\nconst prepared = await prepareStoredFiles(selected, settings);","handlingStrategy":"validation","validationCode":"if (selected.length > settings.maxFiles) { showError(`Stored transfers can contain at most ${settings.maxFiles} files`); return; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Show the maxFiles limit in the picker UI and live-count the selection","Read maxFiles from server-provided settings so client and server caps never diverge"],"tags":["validation","limits","stored-transfer"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}