HeyPuter/puter · error · HttpError
bad_request
bad_request
Error message
`prompt` must be a string
What it means
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.
Source
Thrown at src/backend/drivers/ai-image/providers/openai/OpenAiImageProvider.ts:110
input_images,
input_image_mime_type,
}: IGenerateParams) {
const selectedModel =
this.models().find((m) => m.id === model) ||
this.models().find((m) => m.id === this.getDefaultModel())!;
if (test_mode) {
return 'https://puter-sample-data.puter.site/image_example.png';
}
// Backwards compat: fold singular `input_image` into `input_images`.
if (input_image && (!input_images || input_images.length === 0)) {
input_images = [input_image];
}
const hasInputImages = (input_images?.length ?? 0) > 0;
if (typeof prompt !== 'string') {
throw new HttpError(400, '`prompt` must be a string', {
legacyCode: 'bad_request',
});
}
const validRatios = selectedModel?.allowedRatios;
if (validRatios) {
if (
!ratio ||
!validRatios.some((r) => r.w === ratio.w && r.h === ratio.h)
) {
ratio = validRatios[0]; // Default to the first allowed ratio
}
} else {
// Open-ended size models (gpt-image-2): conform to OpenAI's size
// rules (16px multiples, 3840 cap, 3:1 ratio, pixel budget).
ratio = this.#normalizeGptImage2Ratio(ratio);
}
View on GitHub (pinned to 908ec23eda)
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.
Example fix
// before
await provider.generate({ prompt: formData.prompt }); // could be undefined
// after
if (typeof formData.prompt !== 'string' || formData.prompt.trim().length === 0) {
throw new HttpError(400, 'prompt is required', { legacyCode: 'bad_request' });
}
await provider.generate({ prompt: formData.prompt }); Defensive patterns
Strategy: validation
Validate before calling
function assertStringPrompt(prompt: unknown): asserts prompt is string {
if (typeof prompt !== 'string') {
throw new Error('`prompt` must be a string');
}
}
// Note: also add a non-empty check since this provider allows empty strings:
if (typeof params.prompt !== 'string' || params.prompt.trim().length === 0) {
throw new Error('prompt is required');
} Type guard
function isStringPrompt(prompt: unknown): prompt is string {
return typeof prompt === 'string';
} Try / catch
try {
await provider.generate(params);
} catch (e) {
if (e instanceof HttpError && e.status_code === 400 && e.message.includes('prompt')) {
// coerce or reject prompt upstream
}
throw e;
} Prevention
- 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.
When it happens
Trigger: Calling generate() with prompt: undefined, prompt: null, prompt: 123, prompt: {}, or prompt: []; passing prompt: '' will NOT trigger this (empty string is allowed here).
Common situations: 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.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/a9152dfe7531fe62.
Report an issue: GitHub.