{"record":{"id":"37575aa22fb3d5ef","repo":"can1357/oh-my-pi","slug":"openai-files-api-upload-response-is-missing-a-file","errorCode":null,"errorMessage":"OpenAI Files API upload response is missing a file id","messagePattern":"OpenAI Files API upload response is missing a file id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-openai.ts","lineNumber":45,"sourceCode":"\t\t\tbaseUrl.password === \"\" &&\n\t\t\tbaseUrl.search === \"\" &&\n\t\t\tbaseUrl.hash === \"\" &&\n\t\t\tpathname === \"/v1\"\n\t\t);\n\t} catch {\n\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 *","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-openai.ts#L27-L63","documentation":"Thrown by parseOpenAIFileResponse when the uploaded-file object lacks an id field that is a non-empty string. The id is the primary key for all subsequent Files API operations (retrieve, delete, attaching to requests), so the library refuses to return a handle without it.","triggerScenarios":"Calling upload() when OpenAI's response object has no id, an empty/whitespace id, or id of a non-string type — typically from a differently-shaped error/edge response or a mis-mapped proxy response.","commonSituations":"Proxy or OpenAI-compatible gateway returning partial objects; mocked responses in tests missing fields; upstream API version change altering the field name (e.g. nesting under data); response from a failed upload that returns an object without id.","solutions":["Dump the full response body to confirm the id field is present and named id.","If using an OpenAI-compatible gateway, check whether it wraps the file in another envelope (e.g. {data: {...}}) and unwrap before passing through.","Pin/verify the OpenAI API version your gateway proxies — older/newer versions may change the schema.","Ensure the upload actually succeeded (check HTTP status and that no error object was returned) before parsing.","In tests, use realistic fixtures copied from real OpenAI upload responses."],"exampleFix":"// before: gateway returns envelope\nconst parsed = parseOpenAIFileResponse(body); // body = { data: { id: ... } }\n// after: unwrap first\nconst file = \"data\" in body && body.data ? body.data : body;\nconst parsed = parseOpenAIFileResponse(file);","handlingStrategy":"type-guard","validationCode":"const body = await res.json();\nconst file = \"data\" in body && isObject(body.data) ? body.data : body;\nif (typeof file?.id !== \"string\" || file.id.trim() === \"\") {\n  throw new Error(`upload response has no usable id: ${JSON.stringify(body).slice(0, 200)}`);\n}","typeGuard":"function hasFileId(v: unknown): v is { id: string } & Record<string, unknown> {\n  return isObject(v) && typeof v.id === \"string\" && v.id.trim().length > 0;\n}","tryCatchPattern":"try {\n  const handle = await openaiClient.upload(request);\n} catch (err) {\n  if (String(err?.message).includes(\"missing a file id\")) {\n    throw new Error(\"gateway response missing file id — unwrap {data:{...}} envelope or check API version\");\n  }\n  throw err;\n}","preventionTips":["Unwrap gateway envelopes ({data: ...}) before parsing","Pin and verify the API version your gateway proxies","Keep test fixtures copied from real OpenAI responses","Confirm upload HTTP status was 2xx before parsing the body"],"tags":["validation","openai","file-upload","response-parsing","missing-field"],"backgroundTag":"invalid-api-response-shape","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}