can1357/oh-my-pi · error
tmpfiles option ttl must be between 60 seconds and 48 hours
Error message
tmpfiles option ttl must be between 60 seconds and 48 hours
What it means
After parsing the tmpfiles ttl duration, the runtime clamps validation to the host's accepted range: between 60 seconds and 48 hours (172800 seconds). Durations outside this range throw, since tmpfiles.org will not honor them. Note "24" without a unit means 24 seconds and fails this check.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-anonymous.ts:157
async upload(request: BlobUploadRequest) {
const { url } = await uploadTextUrl("uguu", config, UGUU_UPLOAD_URL, multipartFile(request, "files[]"));
return publication("uguu", request, url, {
expiresAt: Date.now() + 3 * HOUR_MS,
remoteId: remoteName(url),
});
},
};
}
function tmpfilesTtlSeconds(config: DestinationRuntimeConfig): number {
const ttl = optionString(config, "ttl", "1h")?.trim().toLowerCase();
const match = ttl?.match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)?$/);
if (!match) throw new Error("tmpfiles option ttl must be a duration such as 1h");
const amount = Number(match[1]);
const multiplier = match[2] === "d" ? 86_400 : match[2] === "h" ? 3_600 : match[2] === "m" ? 60 : 1;
const seconds = Math.round(amount * multiplier);
if (!Number.isFinite(seconds) || seconds < 60 || seconds > 172_800) {
throw new Error("tmpfiles option ttl must be between 60 seconds and 48 hours");
}
return seconds;
}
function recordValue(value: unknown): Readonly<Record<string, unknown>> | undefined {
return typeof value === "object" && value !== null && !Array.isArray(value)
? (value as Readonly<Record<string, unknown>>)
: undefined;
}
function stringAtPath(value: unknown, path: string): string | undefined {
let current = value;
for (const segment of path.split(".").filter(Boolean)) {
if (Array.isArray(current)) {
if (!/^\d+$/.test(segment)) return undefined;
current = current[Number(segment)];
continue;
}View on GitHub (pinned to 9690622007)
Solutions
- Set ttl between "1m" and "48h" (e.g. "1h", "12h", "48h").
- Add a unit when you mean hours: "24" → "24h".
- For retention beyond 48h, pick a different destination that supports longer expiry.
Example fix
// before
{ "options": { "ttl": "3d" } }
// after
{ "options": { "ttl": "48h" } } Defensive patterns
Strategy: validation
Validate before calling
function parseTtlSeconds(ttl: string): number {
const m = ttl.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(s|m|h|d)?$/);
if (!m) throw new Error(`bad ttl: ${ttl}`);
const mult = m[2] === "d" ? 86400 : m[2] === "h" ? 3600 : m[2] === "m" ? 60 : 1;
return Math.round(Number(m[1]) * mult);
}
const seconds = parseTtlSeconds(String(config.options.ttl ?? "1h"));
if (seconds < 60 || seconds > 172800) throw new Error(`tmpfiles ttl must be 60s–48h, got ${seconds}s`); Type guard
const isAcceptableTtl = (v: unknown): boolean => { try { const s = parseTtlSeconds(String(v)); return s >= 60 && s <= 172800; } catch { return false; } }; Try / catch
try {
await uploadTmpfiles(config, req);
} catch (err) {
if (String(err).includes("between 60 seconds and 48 hours")) {
logger.warn("ttl out of range for tmpfiles; clamping to 48h");
config.options.ttl = "48h";
} else throw err;
} Prevention
- Keep tmpfiles ttl within 1m–48h; clamp user-supplied durations in your own loader.
- Remember a bare number is seconds: "24" is 24s and fails the lower bound.
- For retention beyond 48h, switch to a destination with longer expiry support.
When it happens
Trigger: tmpfiles `ttl` parses as a valid duration but rounds outside [60, 172800] seconds — e.g. "30s", "24" (interpreted as 24 seconds), "3d" (259200s), "100h", or a fractional value like "0.5m" (30s).
Common situations: Users wanting longer retention than the host allows ("3d", "1w"), specifying too-short retention ("10s", "30s"), or forgetting the unit so a number becomes seconds.
Related errors
- tmpfiles option ttl must be a duration such as 1h
- Failed to load tree-sitter language: {err}
- Destination option ${key} must be a string
- Destination option ${key} must be a finite number
- Destination option ${key} must be a boolean
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2a5b263605902fda.
Report an issue: GitHub.