{"record":{"id":"97fa92ee63ad2e9e","repo":"can1357/oh-my-pi","slug":"an-openai-api-credential-is-required-for-file-uplo","errorCode":null,"errorMessage":"An OpenAI API credential is required for file uploads","messagePattern":"An OpenAI API credential is required for file uploads","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/provider-files-openai.ts","lineNumber":73,"sourceCode":"\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,\n\tfetchImpl?: FetchImpl,\n): ProviderFileClient | null {\n\tif (!isOfficialOpenAIResponsesModel(model)) return null;\n\tif (credential.trim().length === 0) throw new Error(\"An OpenAI API credential is required for file uploads\");\n\n\tconst request = fetchImpl ?? globalThis.fetch;\n\tconst authorization = `Bearer ${credential}`;\n\n\treturn {\n\t\tprovider: \"openai\",\n\t\tasync upload(uploadRequest: ProviderFileUploadRequest): Promise<ProviderFileHandle> {\n\t\t\tconst form = new FormData();\n\t\t\tform.append(\"purpose\", \"vision\");\n\t\t\tform.append(\n\t\t\t\t\"file\",\n\t\t\t\tnew Blob([uploadRequest.bytes], { type: uploadRequest.mimeType }),\n\t\t\t\tfileName(uploadRequest),\n\t\t\t);\n\n\t\t\tlet response: Response;\n\t\t\ttry {\n\t\t\t\tresponse = await request(OPENAI_FILES_URL, {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/provider-files-openai.ts#L55-L91","documentation":"Thrown by createOpenAIFileClient when the supplied credential is empty or whitespace-only. The factory only builds an OpenAI file client for official OpenAI Responses models, and since every Files API request requires a Bearer token, it fails fast at construction instead of producing 401s later on each upload.","triggerScenarios":"Calling createOpenAIFileClient (or the higher-level client()) with credential = \"\" or \"   \" while the model is an official OpenAI Responses model — e.g. an unset OPENAI_API_KEY env var read as an empty string, or a config loader that defaults missing keys to \"\".","commonSituations":"OPENAI_API_KEY not set in the environment or .env file not loaded; config file with apiKey: \"\" placeholder; CI environment missing the secret; key rotated/removed but stale empty config remains; passing the wrong variable (e.g. an OAuth token variable that is empty).","solutions":["Set OPENAI_API_KEY (or the credential your wiring passes) to a valid non-empty API key and restart the process.","Check how empty values arise: a dotenv loader may override a real key with an empty entry later in the file — inspect the final value at runtime.","Add a startup check that fails fast when OpenAI models are configured but the key is blank.","In CI, ensure the secret is actually injected into the environment (secrets are not inherited by default in some CI systems).","If OpenAI file upload is not intended, verify the configured model is not an official OpenAI Responses model, since that triggers credential requirement."],"exampleFix":"// before\nconst key = process.env.OPENAI_API_KEY ?? \"\"; // empty when unset\nconst client = createOpenAIFileClient(model, key); // throws\n// after\nconst key = process.env.OPENAI_API_KEY;\nif (!key?.trim()) throw new Error(\"Set OPENAI_API_KEY to use OpenAI file uploads\");\nconst client = createOpenAIFileClient(model, key);","handlingStrategy":"validation","validationCode":"const key = process.env.OPENAI_API_KEY;\nif (typeof key !== \"string\" || key.trim().length === 0) {\n  throw new Error(\"OPENAI_API_KEY is not set; cannot create OpenAI file client\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  const client = createOpenAIFileClient(model, credential);\n} catch (err) {\n  if (String(err?.message).includes(\"credential is required\")) {\n    throw new Error(\"Set OPENAI_API_KEY (non-empty) to enable OpenAI file uploads\");\n  }\n  throw err;\n}","preventionTips":["Check for the credential at process startup, before any OpenAI model is used","Watch for dotenv entries that overwrite a valid key with an empty value","Verify CI actually injects the secret into the job environment","Guard client construction behind an explicit 'file uploads enabled' config flag"],"tags":["configuration","openai","api-key","missing-env-var","file-upload"],"backgroundTag":"missing-api-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}