{"record":{"id":"369075f4ea70c07a","repo":"schollz/croc","slug":"stored-transfers-can-expire-after-at-most-settin","errorCode":null,"errorMessage":"Stored transfers can expire after at most ${settings.maxExpiresSeconds} seconds","messagePattern":"Stored transfers can expire after at most (.+?) seconds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web/src/protocol/stored.ts","lineNumber":596,"sourceCode":"  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\n  ) {\n    throw new Error(\n      `Stored transfers can expire after at most ${settings.maxExpiresSeconds} seconds`,\n    );\n  }\n  const key = await wasm().storeGenerateKey();\n  const plan = planStoredUpload(files);\n  const created = await createStoredUpload(\n    key,\n    plan,\n    downloads,\n    expiresSeconds,\n    settings,\n    callbacks,\n    signal,\n  );\n  let finalized = false;\n  try {\n    await uploadStoredManifest(plan, created, settings, callbacks, signal);\n    await uploadStoredChunks(plan, created, settings, callbacks, signal);","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/stored.ts#L578-L614","documentation":"Thrown by uploadStoredFiles() (stored.ts:592) when expiresSeconds exceeds settings.maxExpiresSeconds, the per-deployment expiry cap advertised by the storage service. maxExpiresSeconds of 0 means unlimited (the check is skipped); any positive value is a hard ceiling. The web app already mins this runtime value against the ~292-year absolute limit in App.tsx and feeds it to the expiration picker.","triggerScenarios":"Calling uploadStoredFiles with expiresSeconds: 7*24*3600 while settings.maxExpiresSeconds is 3*24*3600. Occurs when the UI offers longer expirations than the server allows or settings are stale.","commonSituations":"Server-side maxExpiresSeconds was reduced after the UI was built; settings fixture in tests differs from production; copied settings between environments.","solutions":["Clamp: when settings.maxExpiresSeconds > 0, pass Math.min(requested, settings.maxExpiresSeconds)","Drive the expiration picker maximum from the same settings value (as stored-ui.tsx does)","Confirm settings come from the current service runtime, not a stale constant"],"exampleFix":"// before\nawait uploadStoredFiles({ files, settings, expiresSeconds: 7 * 24 * 3600 });\n\n// after\nconst requested = 7 * 24 * 3600;\nconst expiresSeconds = settings.maxExpiresSeconds > 0 ? Math.min(requested, settings.maxExpiresSeconds) : requested;\nawait uploadStoredFiles({ files, settings, expiresSeconds });","handlingStrategy":"validation","validationCode":"const cap = settings.maxExpiresSeconds;\nconst expiresSeconds = cap > 0 ? Math.min(requested, cap) : requested;","typeGuard":"function withinExpiryLimit(expiresSeconds: number, s: StoredSettings): boolean {\n  return s.maxExpiresSeconds <= 0 || expiresSeconds <= s.maxExpiresSeconds;\n}","tryCatchPattern":"try { await uploadStoredFiles(opts); } catch (e) { if (e instanceof Error && e.message.startsWith('Stored transfers can expire after at most')) { opts.expiresSeconds = settings.maxExpiresSeconds; return uploadStoredFiles(opts); } throw e; }","preventionTips":["Feed settings.maxExpiresSeconds into the picker maximum (as stored-ui.tsx does)","Remember maxExpiresSeconds === 0 means unlimited, not zero"],"tags":["validation","stored-transfer","configuration","upload"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}