{"record":{"id":"14a6e766f271dde0","repo":"Significant-Gravitas/AutoGPT","slug":"expiration-hours-must-be-between-1-and-48","errorCode":null,"errorMessage":"Expiration hours must be between 1 and 48","messagePattern":"Expiration hours must be between 1 and 48","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"autogpt_platform/backend/backend/api/features/v1.py","lineNumber":598,"sourceCode":"    ctx: Annotated[RequestContext, Security(get_request_context)],\n    file: UploadFile = File(...),\n    expiration_hours: int = 24,\n) -> UploadFileResponse:\n    \"\"\"\n    Upload a file to cloud storage and return a storage key that can be used\n    with FileStoreBlock and AgentFileInputBlock.\n\n    Args:\n        file: The file to upload\n        user_id: The user ID\n        provider: Cloud storage provider (\"gcs\", \"s3\", \"azure\")\n        expiration_hours: Hours until file expires (1-48)\n\n    Returns:\n        Dict containing the cloud storage path and signed URL\n    \"\"\"\n    if expiration_hours < 1 or expiration_hours > 48:\n        raise HTTPException(\n            status_code=400, detail=\"Expiration hours must be between 1 and 48\"\n        )\n\n    # Check file size limit before reading content to avoid memory issues\n    max_size_mb = settings.config.upload_file_size_limit_mb\n    max_size_bytes = max_size_mb * 1024 * 1024\n\n    # Try to get file size from headers first\n    if hasattr(file, \"size\") and file.size is not None and file.size > max_size_bytes:\n        raise _create_file_size_error(file.size, max_size_mb)\n\n    # Read file content\n    content = await file.read()\n    content_size = len(content)\n\n    # Double-check file size after reading (in case header was missing/incorrect)\n    if content_size > max_size_bytes:\n        raise _create_file_size_error(content_size, max_size_mb)","sourceCodeStart":580,"sourceCodeEnd":616,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/v1.py#L580-L616","documentation":"HTTP 400 from the cloud-storage upload endpoint when expiration_hours is outside the allowed 1-48 window. The signed URL issued by the provider (gcs/s3/azure) is only valid for that bounded range, so the route rejects anything under 1 hour or over 48 hours before reading the file body.","triggerScenarios":"POST of a file to the upload-to-cloud endpoint with expiration_hours=0, negative, fractional-but-clamped-out values like 0.5, or values > 48 (e.g. 72 for a 3-day link).","commonSituations":"Copy-pasting an expiry in minutes instead of hours (e.g. 30 meaning 30 minutes but sent as 30 hours, or 0.5 meaning half an hour); defaults from another API that allows 7-day links; UI sliders or env config with unbounded ranges.","solutions":["Send an integer expiration_hours between 1 and 48 inclusive","Clamp the user-supplied value in the client before calling the API: Math.min(48, Math.max(1, hours))","If longer-lived files are needed, use the regular file-storage endpoints instead of the signed-URL cloud upload"],"exampleFix":"# before\nexpiration_hours = 0.5  # intended '30 minutes' -> 400\n\n# after\nexpiration_hours = 1  # minimum allowed","handlingStrategy":"validation","validationCode":"const hours = Math.round(Math.min(48, Math.max(1, userHours)));","typeGuard":"function isValidExpirationHours(h: unknown): h is number {\n  return typeof h === \"number\" && Number.isInteger(h) && h >= 1 && h <= 48;\n}","tryCatchPattern":null,"preventionTips":["Document the 1-48 range in the upload UI","Never pass minutes or fractional hours","Clamp at the client boundary before sending"],"tags":["validation","file-upload","cloud-storage","http-400"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}