{"record":{"id":"57ffa0e19f43e36b","repo":"HeyPuter/puter","slug":"bad-request-57ffa0","errorCode":"bad_request","errorMessage":"`prompt` must be a non-empty string","messagePattern":"`prompt` must be a non-empty string","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/drivers/ai-image/providers/gemini/GeminiImageProvider.ts","lineNumber":89,"sourceCode":"    async generate(params: IGenerateParams): Promise<string> {\n        const { prompt, test_mode, input_image, input_image_mime_type, model } =\n            params;\n        let { ratio, input_images, quality } = params;\n\n        const selectedModel =\n            (this.models() as IGeminiImageModel[]).find(\n                (m) => m.id === model,\n            ) ||\n            (this.models() as IGeminiImageModel[]).find(\n                (m) => m.id === this.getDefaultModel(),\n            )!;\n\n        if (test_mode) {\n            return 'https://puter-sample-data.puter.site/image_example.png';\n        }\n\n        if (typeof prompt !== 'string' || prompt.trim().length === 0) {\n            throw new HttpError(400, '`prompt` must be a non-empty string', {\n                legacyCode: 'bad_request',\n            });\n        }\n\n        if (selectedModel.apiType === 'generateImages') {\n            return this.#generateWithImagen(prompt, selectedModel, params);\n        }\n\n        const allowedRatios = selectedModel.allowedRatios ?? [\n            GEMINI_DEFAULT_RATIO,\n        ];\n        ratio =\n            ratio && this.#isValidRatio(ratio, allowedRatios)\n                ? ratio\n                : allowedRatios[0];\n\n        // Backwards compat: merge singular input_image into input_images\n        if (input_image && (!input_images || input_images.length === 0)) {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-image/providers/gemini/GeminiImageProvider.ts#L71-L107","documentation":"Thrown by GeminiImageProvider.generate() (GeminiImageProvider.ts:88) before any upstream API call when the `prompt` field is not a string or contains only whitespace. It is a hard precondition: the Gemini generateContent path needs a text prompt to embed as the first content part, so an empty/missing prompt cannot produce a meaningful request. The check runs after test_mode short-circuit, so test calls never hit it.","triggerScenarios":"Calling provider.generate({}) with no prompt; passing prompt: '' or prompt: '   '; passing prompt: null or prompt: 123; passing prompt: undefined. Any non-string or whitespace-only value reaches line 88 and throws.","commonSituations":"A controller or driver layer forwards user input without validating `prompt`; a caller passes a form field that was left blank; JSON deserialization yields null where a string was expected; programmatic callers defaulting prompt to an empty string.","solutions":["Ensure the caller passes a non-empty string as `prompt` before invoking generate().","Validate at the controller/driver boundary: reject requests where prompt is missing or blank, returning bad_request before reaching the provider.","If building prompts dynamically, guard with `if (!prompt?.trim()) return;` or fall back to a default description."],"exampleFix":"// before\nawait provider.generate({ prompt: userPrompt });\n\n// after\nif (typeof userPrompt !== 'string' || userPrompt.trim().length === 0) {\n  throw new HttpError(400, 'prompt is required', { legacyCode: 'bad_request' });\n}\nawait provider.generate({ prompt: userPrompt });","handlingStrategy":"validation","validationCode":"function assertNonEmptyPrompt(prompt: unknown): asserts prompt is string {\n  if (typeof prompt !== 'string' || prompt.trim().length === 0) {\n    throw new Error('`prompt` must be a non-empty string');\n  }\n}\n// before calling generate():\nassertNonEmptyPrompt(params.prompt);","typeGuard":"function isValidPrompt(prompt: unknown): prompt is string {\n  return typeof prompt === 'string' && prompt.trim().length > 0;\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    // surface a user-friendly validation error\n  }\n  throw e;\n}","preventionTips":["Validate prompt at the API/controller boundary before forwarding to any provider.","Treat prompt as required in your request schema (e.g. zod, JSON schema).","Default to rejecting empty/whitespace input explicitly rather than relying on the provider."],"tags":["validation","input-validation","gemini","ai-image","bad-request"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}