{"record":{"id":"a9152dfe7531fe62","repo":"HeyPuter/puter","slug":"bad-request-a9152d","errorCode":"bad_request","errorMessage":"`prompt` must be a string","messagePattern":"`prompt` must be a string","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/drivers/ai-image/providers/openai/OpenAiImageProvider.ts","lineNumber":110,"sourceCode":"        input_images,\n        input_image_mime_type,\n    }: IGenerateParams) {\n        const selectedModel =\n            this.models().find((m) => m.id === model) ||\n            this.models().find((m) => m.id === this.getDefaultModel())!;\n\n        if (test_mode) {\n            return 'https://puter-sample-data.puter.site/image_example.png';\n        }\n\n        // Backwards compat: fold singular `input_image` into `input_images`.\n        if (input_image && (!input_images || input_images.length === 0)) {\n            input_images = [input_image];\n        }\n        const hasInputImages = (input_images?.length ?? 0) > 0;\n\n        if (typeof prompt !== 'string') {\n            throw new HttpError(400, '`prompt` must be a string', {\n                legacyCode: 'bad_request',\n            });\n        }\n\n        const validRatios = selectedModel?.allowedRatios;\n        if (validRatios) {\n            if (\n                !ratio ||\n                !validRatios.some((r) => r.w === ratio.w && r.h === ratio.h)\n            ) {\n                ratio = validRatios[0]; // Default to the first allowed ratio\n            }\n        } else {\n            // Open-ended size models (gpt-image-2): conform to OpenAI's size\n            // rules (16px multiples, 3840 cap, 3:1 ratio, pixel budget).\n            ratio = this.#normalizeGptImage2Ratio(ratio);\n        }\n","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-image/providers/openai/OpenAiImageProvider.ts#L92-L128","documentation":"Thrown by OpenAiImageProvider.generate() (OpenAiImageProvider.ts:110) when `prompt` is not a string. Unlike the Gemini and Replicate providers, this check only verifies typeof === 'string' — it does NOT reject empty strings, so prompt: '' passes this guard and proceeds to the OpenAI API. The check fires after test_mode and after the input_image/input_images merge.","triggerScenarios":"Calling generate() with prompt: undefined, prompt: null, prompt: 123, prompt: {}, or prompt: []; passing prompt: '' will NOT trigger this (empty string is allowed here).","commonSituations":"Caller omits prompt from the params object (becomes undefined); a deserialization or form-parsing bug yields a non-string; programmatic pipeline passing through an unvalidated variable.","solutions":["Ensure the caller passes a string prompt (validate before calling generate).","Add a non-empty check at the controller boundary since this provider permits empty strings.","Coerce or reject non-string prompt values upstream."],"exampleFix":"// before\nawait provider.generate({ prompt: formData.prompt });  // could be undefined\n\n// after\nif (typeof formData.prompt !== 'string' || formData.prompt.trim().length === 0) {\n  throw new HttpError(400, 'prompt is required', { legacyCode: 'bad_request' });\n}\nawait provider.generate({ prompt: formData.prompt });","handlingStrategy":"validation","validationCode":"function assertStringPrompt(prompt: unknown): asserts prompt is string {\n  if (typeof prompt !== 'string') {\n    throw new Error('`prompt` must be a string');\n  }\n}\n// Note: also add a non-empty check since this provider allows empty strings:\nif (typeof params.prompt !== 'string' || params.prompt.trim().length === 0) {\n  throw new Error('prompt is required');\n}","typeGuard":"function isStringPrompt(prompt: unknown): prompt is string {\n  return typeof prompt === 'string';\n}","tryCatchPattern":"try {\n  await provider.generate(params);\n} catch (e) {\n  if (e instanceof HttpError && e.status_code === 400 && e.message.includes('prompt')) {\n    // coerce or reject prompt upstream\n  }\n  throw e;\n}","preventionTips":["Validate prompt is a string at the boundary (this provider only checks typeof, not emptiness).","Add your own non-empty check since OpenAiImageProvider permits empty strings.","Treat prompt as required in request schemas."],"tags":["validation","input-validation","openai","ai-image","bad-request"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}