{"record":{"id":"295778e747d16ac8","repo":"can1357/oh-my-pi","slug":"tmpfiles-option-ttl-must-be-a-duration-such-as-1h","errorCode":null,"errorMessage":"tmpfiles option ttl must be a duration such as 1h","messagePattern":"tmpfiles option ttl must be a duration such as 1h","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-anonymous.ts","lineNumber":152,"sourceCode":"}\n\nfunction createUguuUploader(config: DestinationRuntimeConfig): BlobUploader {\n\treturn {\n\t\tdestination: \"uguu\",\n\t\tasync upload(request: BlobUploadRequest) {\n\t\t\tconst { url } = await uploadTextUrl(\"uguu\", config, UGUU_UPLOAD_URL, multipartFile(request, \"files[]\"));\n\t\t\treturn publication(\"uguu\", request, url, {\n\t\t\t\texpiresAt: Date.now() + 3 * HOUR_MS,\n\t\t\t\tremoteId: remoteName(url),\n\t\t\t});\n\t\t},\n\t};\n}\n\nfunction tmpfilesTtlSeconds(config: DestinationRuntimeConfig): number {\n\tconst ttl = optionString(config, \"ttl\", \"1h\")?.trim().toLowerCase();\n\tconst match = ttl?.match(/^(\\d+(?:\\.\\d+)?)\\s*(s|m|h|d)?$/);\n\tif (!match) throw new Error(\"tmpfiles option ttl must be a duration such as 1h\");\n\tconst amount = Number(match[1]);\n\tconst multiplier = match[2] === \"d\" ? 86_400 : match[2] === \"h\" ? 3_600 : match[2] === \"m\" ? 60 : 1;\n\tconst seconds = Math.round(amount * multiplier);\n\tif (!Number.isFinite(seconds) || seconds < 60 || seconds > 172_800) {\n\t\tthrow new Error(\"tmpfiles option ttl must be between 60 seconds and 48 hours\");\n\t}\n\treturn seconds;\n}\n\nfunction recordValue(value: unknown): Readonly<Record<string, unknown>> | undefined {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value)\n\t\t? (value as Readonly<Record<string, unknown>>)\n\t\t: undefined;\n}\n\nfunction stringAtPath(value: unknown, path: string): string | undefined {\n\tlet current = value;\n\tfor (const segment of path.split(\".\").filter(Boolean)) {","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-anonymous.ts#L134-L170","documentation":"tmpfilesTtlSeconds parses the tmpfiles `ttl` option as a duration string (number plus optional unit s/m/h/d, e.g. \"1h\", \"90s\", \"2d\") and throws when the format does not match the expected pattern. Unlike litterbox, tmpfiles accepts arbitrary durations, so the option is free-form — but it must still be a recognizable duration.","triggerScenarios":"Configuring tmpfiles `ttl` with text that fails /^\\d+(?:\\.\\d+)?\\s*(s|m|h|d)?$/ — e.g. \"1 hour\" (word unit), \"h1\", \"1w\" (unsupported week unit), \"\", \"24\", or a plain number type rather than a duration string (which would first fail optionString at uploader-runtime.ts:56).","commonSituations":"Writing \"1 week\" or \"1w\" in config, omitting the unit while expecting defaults, or pasting \"1h30m\" compound durations which the simple regex cannot parse.","solutions":["Use a simple duration like \"45m\", \"2h\", or \"1d\" — one number plus one unit (s|m|h|d), unit optional (defaults to seconds).","Spell units as single letters: m for minutes, h for hours, d for days — not \"min\" or \"hour\".","Split compound durations (\"1h30m\") into a single unit (\"90m\")."],"exampleFix":"// before\n{ \"options\": { \"ttl\": \"1 hour\" } }\n// after\n{ \"options\": { \"ttl\": \"1h\" } }","handlingStrategy":"validation","validationCode":"const DURATION = /^\\d+(?:\\.\\d+)?\\s*(s|m|h|d)?$/;\nconst ttl = String(config.options.ttl ?? \"1h\");\nif (!DURATION.test(ttl)) throw new Error(`tmpfiles ttl must match <number><s|m|h|d>, got: ${ttl}`);","typeGuard":"const isSimpleDuration = (v: unknown): v is string => typeof v === \"string\" && /^\\d+(?:\\.\\d+)?\\s*(s|m|h|d)?$/.test(v.trim().toLowerCase());","tryCatchPattern":"try {\n  await uploadTmpfiles(config, req);\n} catch (err) {\n  if (String(err).includes(\"tmpfiles option ttl must be a duration\")) {\n    logger.warn(\"invalid ttl format; using 1h default\");\n    config.options.ttl = \"1h\";\n  } else throw err;\n}","preventionTips":["Use single-letter units only: s, m, h, d — never \"min\", \"hour\", \"week\".","One number + one optional unit; split compound durations like 1h30m into 90m.","Trim whitespace and lowercase the value in your own config loader."],"tags":["config","validation","duration"],"backgroundTag":"invalid-duration-format","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}