schollz/croc · error
Stored-transfer expiration must be a whole number of seconds
Error message
Stored-transfer expiration must be a whole number of seconds of at least one minute
What it means
Thrown by uploadStoredFiles() (stored.ts:583) when `expiresSeconds` is not a safe integer, is under 60 (one minute), or exceeds 9,223,372,036 seconds (~292 years, the int64 ceiling the backend accepts). The service stores expiry as whole seconds in an int64, so fractional, sub-minute, or astronomically large values are rejected before the upload starts.
Source
Thrown at web/src/protocol/stored.ts:588
downloads = 1,
expiresSeconds = 24 * 60 * 60,
callbacks = {},
signal,
} = options;
if (!Number.isSafeInteger(downloads) || downloads < 1) {
throw new Error("Stored-transfer downloads must be a positive integer");
}
if (downloads > settings.maxDownloads) {
throw new Error(
`Stored transfers can allow at most ${settings.maxDownloads} downloads`,
);
}
if (
!Number.isSafeInteger(expiresSeconds) ||
expiresSeconds < 60 ||
expiresSeconds > 9_223_372_036
) {
throw new Error(
"Stored-transfer expiration must be a whole number of seconds of at least one minute",
);
}
if (
settings.maxExpiresSeconds > 0 &&
expiresSeconds > settings.maxExpiresSeconds
) {
throw new Error(
`Stored transfers can expire after at most ${settings.maxExpiresSeconds} seconds`,
);
}
const key = await wasm().storeGenerateKey();
const plan = planStoredUpload(files);
const created = await createStoredUpload(
key,
plan,
downloads,
expiresSeconds,View on GitHub (pinned to e25f1bdc04)
Solutions
- Round and floor at 60: expiresSeconds = Math.max(60, Math.floor(v)), then re-check Number.isSafeInteger
- Compute expiry from integer units (integer hours * 3600)
- Coerce possible strings with Number() before validating
Example fix
// before
await uploadStoredFiles({ files, settings, expiresSeconds: minutes * 60 });
// after
const expiresSeconds = Math.max(60, Math.round(minutes) * 60);
await uploadStoredFiles({ files, settings, expiresSeconds }); Defensive patterns
Strategy: validation
Validate before calling
const expiresSeconds = Math.round(Number(raw));
if (!Number.isSafeInteger(expiresSeconds) || expiresSeconds < 60 || expiresSeconds > 9_223_372_036) {
throw new RangeError('expiration must be whole seconds in [60, 9223372036]');
} Type guard
function isValidExpiration(v: unknown): v is number {
return typeof v === 'number' && Number.isSafeInteger(v) && v >= 60 && v <= 9_223_372_036;
} Try / catch
try { await uploadStoredFiles(opts); } catch (e) { if (e instanceof Error && e.message.includes('whole number of seconds')) { flagField('expiration'); return; } throw e; } Prevention
- Compute expiry from integer minutes/hours only
- Enforce a 60-second minimum in the UI
When it happens
Trigger: Calling uploadStoredFiles({ expiresSeconds: 30 }), with a fractional value like 86400.5, NaN, Infinity, or anything above 9_223_372_036. Also when expiry is computed from a float (fractional minutes * 60) or passed as a string ('86400' fails Number.isSafeInteger).
Common situations: UI permits '0 minutes'; expiry derived from a millisecond Date.now() delta without rounding; values deserialized from JSON config left as strings.
Related errors
- Stored-transfer downloads must be a positive integer
- Stored transfers can allow at most ${settings.maxDownloads}
- Stored transfers can expire after at most ${settings.maxExpi
- Invalid stored-transfer id
- Stored transfers can contain at most ${settings.maxFiles} fi
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/4eee81f7decffd89.
Report an issue: GitHub.