Skip to content

AI Gateway silently ignores mask parameter for OpenAI inpainting (gpt-image-1) #14360

Description

@mttzzz

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:

  1. ai core (generate-image.tsnormalizePrompt()): Correctly extracts mask from the prompt object
  2. @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) }
  3. 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:

  1. Detect that this is an inpainting request (image + mask present)
  2. Route to OpenAI's /v1/images/edits endpoint (not /v1/images/generations)
  3. 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.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions