{"record":{"id":"ac59859d4d60ad48","repo":"can1357/oh-my-pi","slug":"openai-files-api-upload-response-has-an-invalid-st","errorCode":null,"errorMessage":"OpenAI Files API upload response has an invalid status","messagePattern":"OpenAI Files API upload response has an invalid status","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-openai.ts","lineNumber":51,"sourceCode":"\t\treturn false;\n\t}\n}\n\nfunction parseOpenAIFileResponse(payload: unknown): OpenAIFileResponse {\n\tconst file = payload as Partial<OpenAIFileResponse> | null;\n\tif (file === null || typeof file !== \"object\") {\n\t\tthrow new Error(\"OpenAI Files API returned an invalid upload response\");\n\t}\n\n\tconst { id, bytes, status } = file;\n\tif (typeof id !== \"string\" || id.trim().length === 0) {\n\t\tthrow new Error(\"OpenAI Files API upload response is missing a file id\");\n\t}\n\tif (typeof bytes !== \"number\" || !Number.isSafeInteger(bytes) || bytes < 0) {\n\t\tthrow new Error(\"OpenAI Files API upload response has an invalid byte count\");\n\t}\n\tif (status !== \"uploaded\" && status !== \"processed\" && status !== \"error\") {\n\t\tthrow new Error(\"OpenAI Files API upload response has an invalid status\");\n\t}\n\treturn { id, bytes, status };\n}\n\nfunction fileName(request: ProviderFileUploadRequest): string {\n\tconst preferred = request.filename?.trim().replaceAll(\"\\\\\", \"/\").split(\"/\").pop();\n\treturn preferred && preferred !== \".\" && preferred !== \"..\" ? preferred : \"image\";\n}\n\n/**\n * Create an OpenAI Files API client for an official OpenAI Responses model.\n *\n * Models using Codex, Azure, OpenRouter, or another OpenAI-compatible endpoint\n * are rejected locally by returning `null`; no request is attempted for them.\n */\nexport function createOpenAIFileClient(\n\tmodel: Model,\n\tcredential: string,","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-openai.ts#L33-L69","documentation":"Thrown by parseOpenAIFileResponse when the status field is not one of the accepted values \"uploaded\", \"processed\", or \"error\". The library whitelists the known OpenAI file statuses; anything else (missing, different casing, or gateway-specific values like \"completed\" or \"pending\") is rejected because downstream logic branches on these exact strings.","triggerScenarios":"Calling upload() when the response status field is absent, an unexpected value such as \"pending\", \"completed\", \"failed\", or different casing (\"Uploaded\") — most common with OpenAI-compatible gateways or batch/asynchronous upload flows that report intermediate states.","commonSituations":"Using an OpenAI-compatible server that emits its own status vocabulary; proxies that lowercase/transform fields; uploading to an endpoint that returns the pre-processing state; outdated gateway versions predating these status values.","solutions":["Compare the raw response's status against the accepted set and map gateway-specific values (e.g. \"completed\" → \"processed\") before parsing.","If the gateway reports an intermediate status, poll the file retrieve endpoint until it reaches a terminal state.","Upgrade the OpenAI-compatible gateway to one matching the official Files API status values.","Check for casing differences and normalize with String(status).toLowerCase() only if you control the mapping both ways.","Update mocked test fixtures to use exactly \"uploaded\", \"processed\", or \"error\"."],"exampleFix":"// before: gateway says \"completed\"\nconst parsed = parseOpenAIFileResponse(body); // throws\n// after: map known aliases\nconst aliases: Record<string, string> = { completed: \"processed\", ok: \"processed\" };\nconst parsed = parseOpenAIFileResponse({\n  ...body,\n  status: aliases[body.status] ?? body.status,\n});","handlingStrategy":"validation","validationCode":"const ACCEPTED = new Set([\"uploaded\", \"processed\", \"error\"]);\nif (typeof body.status !== \"string\" || !ACCEPTED.has(body.status)) {\n  throw new Error(`unsupported file status: ${String(body.status)}`);\n}","typeGuard":"function hasKnownFileStatus(v: unknown): v is { status: \"uploaded\" | \"processed\" | \"error\" } & Record<string, unknown> {\n  return isObject(v) && (v.status === \"uploaded\" || v.status === \"processed\" || v.status === \"error\");\n}","tryCatchPattern":null,"preventionTips":["Map gateway status aliases (\"completed\" → \"processed\") before parsing","Poll the retrieve endpoint until a terminal status instead of parsing intermediate states","Normalize casing only via an explicit two-way mapping","Keep fixtures using exactly uploaded/processed/error"],"tags":["validation","openai","file-upload","response-parsing","enum-mismatch"],"backgroundTag":"invalid-api-response-shape","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}