Description
When using generateImage() with @ai-sdk/gateway and providing a mask parameter for OpenAI inpainting (gpt-image-1), the Gateway silently ignores the mask and generates a completely new image instead of editing only the masked region.
The same image + mask + prompt sent directly to OpenAI's /v1/images/edits endpoint produces correct inpainting — only the masked area is regenerated while unmasked areas are preserved.
Reproduction
import { gateway } from '@ai-sdk/gateway';
import { generateImage } from 'ai';
const result = await generateImage({
model: gateway.imageModel('openai/gpt-image-1'),
prompt: {
text: 'Premium food photography of a gourmet burger...',
images: [sourceImageUint8Array],
mask: maskUint8Array, // RGBA PNG, transparent=edit, opaque=preserve
},
size: '1024x1536',
providerOptions: {
openai: { quality: 'high' },
},
});
Evidence
Setup:
- Source image: 1024×1536 PNG photo of a burger with a branded flag on top
- Mask: 1024×1536 RGBA PNG, ~15.7% transparent (ellipse over the burger area), rest opaque (preserve flag, fries, background)
Via Gateway — mask completely ignored, entire image regenerated from scratch:
- The flag, fries, background, composition — everything is different
- No warnings or errors returned
Via direct OpenAI API — mask correctly applied:
const formData = new FormData();
formData.append('model', 'gpt-image-1');
formData.append('image', imageFile);
formData.append('mask', maskFile);
formData.append('prompt', prompt);
formData.append('size', '1024x1536');
formData.append('quality', 'high');
await fetch('https://api.openai.com/v1/images/edits', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
body: formData,
});
- Only the burger (masked area) was regenerated
- Flag, fries, background — preserved from original
Analysis
I traced the code through the SDK:
ai core (generate-image.ts → normalizePrompt()): Correctly extracts mask from the prompt object
@ai-sdk/gateway client (index.js ~line 1030): Correctly encodes mask via maybeEncodeImageFile() and includes it in the JSON body sent to the Gateway server:
...mask && { mask: maybeEncodeImageFile(mask) }
- Gateway server: Receives the mask in the request body but appears to route to OpenAI's
/v1/images/generations instead of /v1/images/edits, silently dropping the mask parameter.
Server-side logs confirm the mask reaches generateImage():
Prompt type: object, keys: text,images,mask
Expected Behavior
When mask is provided in the prompt, the Gateway should:
- Detect that this is an inpainting request (image + mask present)
- Route to OpenAI's
/v1/images/edits endpoint (not /v1/images/generations)
- Send the request as multipart/form-data with
image and mask files (as required by OpenAI's edits API)
Actual Behavior
The Gateway ignores the mask parameter entirely and generates a completely new image as if no mask was provided. No warning or error is returned.
Versions
ai: 6.0.158
@ai-sdk/gateway: 3.0.95
- Node/Bun: Bun 1.3.11
- OS: Windows (production: Linux)
Additional context
This is related to how OpenAI's image API has two separate endpoints:
/v1/images/generations — text-to-image (no mask support)
/v1/images/edits — inpainting (requires image + mask as multipart files)
The Gateway needs to dispatch to the correct endpoint based on whether a mask is present in the request.
Description
When using
generateImage()with@ai-sdk/gatewayand providing amaskparameter for OpenAI inpainting (gpt-image-1), the Gateway silently ignores the mask and generates a completely new image instead of editing only the masked region.The same image + mask + prompt sent directly to OpenAI's
/v1/images/editsendpoint produces correct inpainting — only the masked area is regenerated while unmasked areas are preserved.Reproduction
Evidence
Setup:
Via Gateway — mask completely ignored, entire image regenerated from scratch:
Via direct OpenAI API — mask correctly applied:
Analysis
I traced the code through the SDK:
aicore (generate-image.ts→normalizePrompt()): Correctly extractsmaskfrom the prompt object@ai-sdk/gatewayclient (index.js~line 1030): Correctly encodes mask viamaybeEncodeImageFile()and includes it in the JSON body sent to the Gateway server:/v1/images/generationsinstead of/v1/images/edits, silently dropping the mask parameter.Server-side logs confirm the mask reaches
generateImage():Expected Behavior
When
maskis provided in the prompt, the Gateway should:/v1/images/editsendpoint (not/v1/images/generations)imageandmaskfiles (as required by OpenAI's edits API)Actual Behavior
The Gateway ignores the
maskparameter entirely and generates a completely new image as if no mask was provided. No warning or error is returned.Versions
ai: 6.0.158@ai-sdk/gateway: 3.0.95Additional context
This is related to how OpenAI's image API has two separate endpoints:
/v1/images/generations— text-to-image (no mask support)/v1/images/edits— inpainting (requires image + mask as multipart files)The Gateway needs to dispatch to the correct endpoint based on whether a mask is present in the request.