{"record":{"id":"154d4b2b40597170","repo":"schollz/croc","slug":"stored-transfer-downloads-must-be-a-positive-integ","errorCode":null,"errorMessage":"Stored-transfer downloads must be a positive integer","messagePattern":"Stored-transfer downloads must be a positive integer","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web/src/protocol/stored.ts","lineNumber":576,"sourceCode":"\nexport async function uploadStoredFiles(options: {\n  files: StoredPreparedFile[];\n  settings: StoredSettings;\n  downloads?: number;\n  expiresSeconds?: number;\n  callbacks?: StoredUploadCallbacks;\n  signal?: AbortSignal;\n}) {\n  const {\n    files,\n    settings,\n    downloads = 1,\n    expiresSeconds = 24 * 60 * 60,\n    callbacks = {},\n    signal,\n  } = options;\n  if (!Number.isSafeInteger(downloads) || downloads < 1) {\n    throw new Error(\"Stored-transfer downloads must be a positive integer\");\n  }\n  if (downloads > settings.maxDownloads) {\n    throw new Error(\n      `Stored transfers can allow at most ${settings.maxDownloads} downloads`,\n    );\n  }\n  if (\n    !Number.isSafeInteger(expiresSeconds) ||\n    expiresSeconds < 60 ||\n    expiresSeconds > 9_223_372_036\n  ) {\n    throw new Error(\n      \"Stored-transfer expiration must be a whole number of seconds of at least one minute\",\n    );\n  }\n  if (\n    settings.maxExpiresSeconds > 0 &&\n    expiresSeconds > settings.maxExpiresSeconds","sourceCodeStart":558,"sourceCodeEnd":594,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/stored.ts#L558-L594","documentation":"Thrown by uploadStoredFiles() (web/src/protocol/stored.ts:575) when the `downloads` option is not a safe positive integer. The library validates client-side before any network call because the storage service only permits whole download counts starting at 1. Values like 0, -1, 2.5, NaN, Infinity, or undefined all fail the Number.isSafeInteger / < 1 checks.","triggerScenarios":"Calling uploadStoredFiles({ files, settings, downloads: X }) with X = 0, negative, fractional (e.g. 1.5), NaN, Infinity, or undefined/non-number. Typical when the value comes from a UI slider defaulting to 0, from parseInt of user input, or from untyped deserialized JSON.","commonSituations":"A number-of-downloads picker whose default is 0 before the user selects; reading the field out of localStorage/sessionStorage receipts where it may be missing; passing a string like '3' (Number.isSafeInteger('3') is false).","solutions":["Validate/clamp before the call: whole number >= 1 (the option defaults to 1, so omit it when unsure)","Derive from UI input with Math.max(1, Math.floor(value)) and re-check Number.isSafeInteger","Type-guard values deserialized from JSON before passing them into options"],"exampleFix":"// before\nawait uploadStoredFiles({ files, settings, downloads: Number(input.value) });\n\n// after\nconst downloads = Number(input.value);\nif (!Number.isSafeInteger(downloads) || downloads < 1) throw new RangeError('downloads must be a whole number >= 1');\nawait uploadStoredFiles({ files, settings, downloads });","handlingStrategy":"validation","validationCode":"const downloads = Number(raw);\nif (!Number.isSafeInteger(downloads) || downloads < 1) {\n  throw new RangeError(`downloads must be a whole number >= 1, got ${raw}`);\n}","typeGuard":"function isValidDownloadCount(v: unknown): v is number {\n  return typeof v === 'number' && Number.isSafeInteger(v) && v >= 1;\n}","tryCatchPattern":"try { await uploadStoredFiles(opts); } catch (e) { if (e instanceof Error && e.message === 'Stored-transfer downloads must be a positive integer') { flagField('downloads'); return; } throw e; }","preventionTips":["Clamp UI values with Math.floor and a >=1 floor before submit","Type-guard anything deserialized from JSON before it reaches options"],"tags":["validation","stored-transfer","typescript","upload"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}