# GPT Image 2の画像編集API：variations移行とdall-e-2エラーの対処

> GPT Image 2のvariationsからeditsへの移行と、images.editで「Value must be 'dall-e-2'」が出る場合の切り分けを解説。参照画像から別案を作る指示、Node.js・Pythonの実装、保存、JSON入力、マスク、連続編集まで扱います。

- Source: https://www.aifreeapi.com/ja/posts/openai-image-editing-api
- Language: ja
- Published: 2026-03-23
- Updated: 2026-09-08
- Publisher: AI Free API (https://www.aifreeapi.com)

`client.images.edit({ model: "gpt-image-2", ... })` で **`Value must be 'dall-e-2'`** が返っても、「GPT Image 2では画像を編集できない」という意味にはなりません。2026年9月8日時点の公式ガイドは、GPT Image 2による直接の画像編集を案内しています。一方、DALL·E 2はAPIから削除されており、エラー文に合わせて `model` を戻すことは現在の解決策になりません。[GPT Image 2のモデル情報](https://developers.openai.com/api/docs/models/gpt-image-2)、[DALL·E 2の提供状況](https://developers.openai.com/api/docs/models/dall-e-2)

元画像から別案を作る場合も、GPT Image 2では `images.edit()` を使います。旧 `/v1/images/variations` は対応していないため、まず実際の送信先を確認してください。すでにeditsを使っている場合は、マスクや古いオプションを外した編集リクエストを同じ送信先で比較し、画像データを保存できるところまで確認します。その後でマスクを追加すれば、モデル指定の拒否と部分編集の問題を混同せずに調べられます。以下は公式資料に基づく実装例です。有料APIへの実行結果を示すものではありません。

## エラー文だけでDALL·E 2へ変更しない

### variationsを呼んでいるなら、操作と指示を変える

[variationsの公式リファレンス](https://developers.openai.com/api/reference/resources/images/methods/create_variation)では、対応モデルはDALL·E 2だけと明記されています。共通の `ImageModel` 型に `gpt-image-2` が載っていても、すべての画像操作に対応するという意味ではありません。確認するのはメソッドごとの対応説明です。

旧コードからの移行では、HTTPのパスを `/v1/images/variations` から `/v1/images/edits` に、SDKの操作を `images.edit()` に変更します。そのうえで `model="gpt-image-2"`、参照画像、**空でない意味のある `prompt`** を渡します。モデル名の差し替えだけでは足りません。[現在のeditsリファレンス](https://developers.openai.com/api/reference/resources/images/methods/edit)

たとえば「同じ部屋を基に、家具の配置は保ち、ポスターの色とデザインを変える」のように、元画像からどんな別案を作るかを指定します。`n` を増やすのは出力枚数の指定です。旧variationsのように、指示を渡さずに別案を得る操作を再現する設定ではありません。

### すでにeditsなら、過去の不具合と現在の入力を分ける

このエラーには、過去に実際の検証不具合が報告されています。2026年4月27日の [openai-node issue #1844](https://github.com/openai/openai-node/issues/1844) では、Node SDK 6.34.0の利用者が `gpt-image-2` の編集で同じモデル指定エラーを報告しました。SDKを使わないcURLでも再現し、リクエストに `response_format` は含まれていませんでした。

その後、8月13日（UTC）の返信で画像編集の検証処理を上流側で修正したとの説明があり、issueは完了として閉じられています。これは過去の問題が修正されたという記録です。現在のすべての接続先で同じ不具合が続いているという根拠にはなりません。また、修正を含むSDKの最低バージョンがここから分かるわけでもありません。[修正に関する返信](https://github.com/openai/openai-node/issues/1844#issuecomment-5285642026)

別の2026年3月のコミュニティ報告では、`response_format` を取り除いて解決した例と、メモリ上の画像にファイル名を付けて解決した例が出ています。異なる条件の報告なので、どちらか一つを万能な修正として適用しないでください。[当時の報告](https://community.openai.com/t/edit-endpoint-images-edits-refusing-gpt-image-models/1375581)

現在のコードで最初に見直す項目は次のとおりです。

| 項目 | GPT Image 2の編集での扱い |
| --- | --- |
| `model` | `gpt-image-2` を明示する |
| `response_format` | 省略する。返された `b64_json` をデコードする |
| `output_format` | PNG・JPEG・WebPなど、画像の符号化形式を指定する |
| `input_fidelity` | 省略する。GPT Image 2では高忠実度の処理が自動で有効になり、変更できない |
| ファイル送信 | 以下の例では実ファイルをmultipartで送る。ファイル名とMIMEタイプも確認する |
| 送信先 | 実際のホストと `/v1/images/edits` を確認する |

`response_format="b64_json"` と `output_format="png"` は別の指定です。前者は旧来の応答形式オプション、後者は保存する画像の形式を決めます。画像の拡張子も後者に合わせます。[APIリファレンス](https://developers.openai.com/api/reference/ruby/resources/images/methods/edit)、[入力忠実度の説明](https://developers.openai.com/api/docs/guides/image-generation#image-input-fidelity)

## まず1枚を編集してPNGとして保存する

例では、手元の `room.png` に写る壁のポスターを差し替えます。入力画像と、実行するサービス用のAPIキーを用意してください。プロジェクトに `openai` パッケージをインストールし、コードを `.mjs` ファイルとして保存するか、ESモジュールを有効にしたプロジェクトで実行します。以下のNode.jsコードはOpenAI公式APIに送信します。

```js
import fs from "node:fs";
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const result = await client.images.edit({
  model: "gpt-image-2",
  image: fs.createReadStream("room.png"),
  prompt:
    "室内写真の壁にあるポスターを、木製額縁に入った抽象画に差し替えてください。" +
    "家具の配置、照明、床の影、カメラの角度を保ち、自然な室内写真にしてください。",
  output_format: "png",
});

const encoded = result.data?.[0]?.b64_json;
if (!encoded) {
  throw new Error("応答に画像データがありません。保存を中止しました。");
}

const imageBytes = Buffer.from(encoded, "base64");
fs.writeFileSync("room-edited.png", imageBytes);
console.log(`room-edited.png に ${imageBytes.length} bytes を保存しました。`);
```

画像編集では「何を変えるか」に加えて、完成画像に残したい要素も記述します。ただし、プロンプトに「保つ」と書くこと自体が完全保持の保証になるわけではありません。まず出力ファイルを開き、希望した変更と、家具・照明などの変化を分けて確認してください。[公式の画像編集例](https://developers.openai.com/api/docs/guides/image-generation#edit-images)

複数の参照画像を使う場合は、`image` にファイルの配列を渡し、「1枚目の部屋に2枚目のポスターを配置する」のように役割を区別します。参照画像から新しい構図を作る用途も編集に含まれます。単に参照画像を添えただけでは、どこまで元の構図を保つかは伝わりません。

### Pythonで実行する場合

Pythonでも同じパラメータで編集し、`b64_json` をバイト列に戻して保存します。ファイルはコンテキストマネージャーで閉じます。

```python
import base64
from pathlib import Path
from openai import OpenAI

client = OpenAI()

with open("room.png", "rb") as image_file:
    result = client.images.edit(
        model="gpt-image-2",
        image=image_file,
        prompt=(
            "室内写真の壁にあるポスターを、木製額縁に入った抽象画に差し替えてください。"
            "家具の配置、照明、床の影、カメラの角度を保ち、自然な室内写真にしてください。"
        ),
        output_format="png",
    )

if not result.data or not result.data[0].b64_json:
    raise RuntimeError("応答に画像データがありません。保存を中止しました。")

image_bytes = base64.b64decode(result.data[0].b64_json, validate=True)
Path("room-edited.png").write_bytes(image_bytes)
print(f"room-edited.png に {len(image_bytes)} bytes を保存しました。")
```

## 参照画像から別案を作るには、変更してよい範囲を決める

`images.edit()` は小さな部分修正だけでなく、参照画像の特徴を使って新しい案を作る場合にも使えます。何を残し、どこに変化を付けるかを分けると、出力を比較して選びやすくなります。

| 作りたい案 | 保ちたいもの | 変えてよいもの |
| --- | --- | --- |
| 同じ部屋のポスター違い | 家具の配置、光、撮影位置 | ポスターの図柄と配色 |
| 商品写真の背景違い | 商品の形、色、ロゴ、向き | 背景、背景に合わせた影 |
| 広告向けの横長構図 | 商品の特徴 | 商品の位置、見出しを置く余白 |
| 同じ場面の水彩イラスト | 登場人物、場面の内容 | 彩色、筆の質感 |

この区別を文章にして `prompt` に渡します。「もっとよくして」だけで済ませず、完成像を具体的に指定してください。一度にすべてを変えるより、まず一つの変更を試し、採用できる結果から次の修正へ進めると判断しやすくなります。高忠実度の処理でも、顔やロゴ、文字の完全な一致までは保証されません。

## 最小のHTTPリクエストと比べて原因を絞る

![モデル指定、ファイル送信、画像保存、編集結果を分けて確認する診断の流れ](https://www.aifreeapi.com/posts/ja/openai-image-editing-api/img/edit-diagnostics.webp)

アプリの処理が複雑な場合は、同じ画像・モデル・送信先を使い、SDKやラッパーを通さないリクエストと比較します。次はOpenAI公式API向けのファイルアップロード例です。`curl -F` がmultipartの境界を設定するため、`Content-Type` ヘッダーを手作業で追加する必要はありません。

```bash
curl --silent --show-error --fail-with-body \
  https://api.openai.com/v1/images/edits \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -F "model=gpt-image-2" \
  -F "image=@room.png;type=image/png" \
  -F "prompt=壁のポスターを木製額縁の抽象画に差し替え、家具と照明を保ってください。" \
  -F "output_format=png" \
  -D edit-headers.txt \
  -o edit-response.json
```

コマンドが成功したら、次の処理で画像だけを取り出せます。JSON応答をそのまま `.png` に改名しても画像にはなりません。

```python
import base64
import json
from pathlib import Path

payload = json.loads(Path("edit-response.json").read_text())
if payload.get("error"):
    raise RuntimeError(payload["error"])

images = payload.get("data") or []
encoded = images[0].get("b64_json") if images else None
if not encoded:
    raise RuntimeError("画像データがありません。応答内容を確認してください。")

Path("room-edited.png").write_bytes(
    base64.b64decode(encoded, validate=True)
)
```

この例はローカルファイルをmultipartで送る方法です。現在の [editsリファレンス](https://developers.openai.com/api/reference/resources/images/methods/edit)には、JSONの `images` 配列に `image_url` または `file_id` を渡す形式も定義されています。`image_url` は完全なURLまたは画像を含むbase64のdata URL、`file_id` はアップロード済みファイルのIDです。

JSONに `"image": "room.png"` と書いても、手元のファイルはアップロードされません。ローカル画像なら上のファイル送信例を使い、URLやファイルIDを使う場合はJSONの仕様に合わせます。SDKの対応パラメータはバージョンによって異なる場合があるため、HTTPの `images` 形式を古いSDKの `image` 引数へそのまま移さないでください。

比較するときは一度に一つだけ条件を変えます。別のサービスへ切り替えると、入力だけでなく認証・対応モデル・中継処理も変わり、原因を絞れなくなります。互換サービスを利用している場合は、そのサービスの送信先と専用キーを使って比較してください。OpenAI用のキーを別ホストへ流用しないでください。

| 比較結果 | 次に確認する箇所 |
| --- | --- |
| 実際のパスが `/v1/images/variations` だった | editsへ変更し、参照画像と空でない編集指示を送る |
| 最小cURLは成功し、アプリだけが失敗 | SDKのバージョン、ラッパーが追加する項目、送信時のファイル情報 |
| cURLもアプリも同じモデル指定エラー | 実際の送信先、モデル対応、サーバー側の検証。リクエストIDを添えて問い合わせる |
| マスクなしは成功し、追加すると失敗 | マスクの形式、サイズ、寸法、アルファチャンネル |
| HTTPは成功したが画像を保存できない | `data` と `b64_json` の有無、デコード処理、保存先 |
| 画像は開けるがマスク外も変化する | リクエスト形式ではなく編集結果の問題。保持要件と画像処理方法を見直す |

これは診断のための比較手順です。cURLが成功しただけでSDK自体の不具合が確定するわけではなく、アプリが送った内容との差を追う必要があります。

### メモリ上の画像はファイル名とMIMEタイプを明示する

現在のPython SDKは `bytes`、`PathLike`、ファイル名・内容・MIMEタイプの組を受け付けます。Node SDKも `File`、`Response`、`fs.ReadStream`、`toFile` などを扱います。「メモリ上のバイト列は使えない」「必ずディスクへ保存しなければならない」と一律に判断するのは適切ではありません。[Python SDKのファイル送信](https://github.com/openai/openai-python#file-uploads)、[Node SDKのファイル送信](https://github.com/openai/openai-node#file-uploads)

実ファイルでは成功してメモリ上の画像だけが失敗するなら、同じ画像データにファイル名とMIMEタイプを与えて比較します。Pythonなら `image=("room.png", image_bytes, "image/png")`、Nodeなら `image: await toFile(imageBytes, "room.png", { type: "image/png" })` が使えます。Nodeの `toFile` は `openai` パッケージからインポートします。

問い合わせに残す情報は、発生時刻とタイムゾーン、送信先、SDKのバージョン、HTTPステータス、`error.message`・`error.param`・`error.code`、レスポンスヘッダーのリクエストIDです。画像の形式・寸法・容量と、ディスク上のファイルかメモリ上のデータかも添えると比較しやすくなります。APIキーや機密画像、画像全体のbase64文字列はログに含めないでください。

## マスクは「編集したい領域」を伝えるために使う

![室内写真のポスター部分を透明なマスクで指定し、編集結果と並べた説明図](https://www.aifreeapi.com/posts/ja/openai-image-editing-api/img/mask-guidance.webp)

モデル指定の問題が解消したら、位置の指定が必要な編集にマスクを加えます。マスクの**透明な部分（アルファ値0）が編集対象**です。黒と白で塗り分けたRGB画像を作っただけでは、透明領域を指定したことになりません。

互換性を確認しやすい入力として、元画像をPNGにそろえ、同じピクセル寸法のRGBA PNGマスクを用意します。複数の参照画像を渡す場合、マスクは最初の画像に適用されます。

| 確認項目 | この例で用意するもの |
| --- | --- |
| 元画像 | `room.png` |
| マスク | `mask.png`。アルファチャンネル付きPNG |
| 寸法・形式 | 元画像とマスクを同じ寸法・PNG形式にする |
| 編集領域 | 差し替えるポスター部分を透明にする |
| マスクの容量 | 4MB未満にする |

公式リファレンスでは参照画像はPNG・WebP・JPGで各50MB未満、最大16枚、マスクはPNGで4MB未満とされています。一方、ガイドのマスク説明には50MB未満という総称的な記載もあるため、ここではマスクに対する厳しい方の条件を採用しています。[編集パラメータのリファレンス](https://developers.openai.com/api/reference/ruby/resources/images/methods/edit)、[マスク編集ガイド](https://developers.openai.com/api/docs/guides/image-generation#edit-an-image-using-a-mask)

先ほどのNode.js例の編集呼び出しを次に置き換え、保存部分はそのまま使えます。

```js
const result = await client.images.edit({
  model: "gpt-image-2",
  image: fs.createReadStream("room.png"),
  mask: fs.createReadStream("mask.png"),
  prompt:
    "透明にした壁のポスター部分に、木製額縁の抽象画を配置してください。" +
    "室内全体は自然な写真とし、家具、照明、床の影、カメラの角度を保ってください。",
  output_format: "png",
});
```

ここでも `input_fidelity="high"` は追加しません。GPT Image 2では高忠実度の処理が自動で行われます。

### マスクが受理されても、外側の画素が固定されるとは限らない

公式ガイドは、GPT Imageのマスクは生成を導くものであり、その形に完全には従わない可能性があると説明しています。したがって、APIが画像を返したかどうかと、商品・顔・文字・背景が必要な精度で保たれたかは別々に判断します。

たとえば商品写真の背景だけを変える用途では、まずラベルの文字、輪郭、反射、色を元画像と比較します。変えたくない部分をプロンプトに明示することは有用ですが、それだけで元画素と一致するわけではありません。

**マスク外の画素を完全に維持することが要件なら、最終画像を合成する処理も検討してください。** 生成結果から必要な領域を取り出し、元画像の上へ重ねれば、採用しない領域は元の画素を残せます。ただし境界のぼかし、影、色のつながりは別途調整が必要です。これはAPIの保証ではなく、厳密な保持が必要な場合の実装上の選択肢です。

## 保存した画像を引き継いで修正する

単発の `images.edit()` を繰り返す場合は、採用した `room-edited.png` を次回の入力画像として渡します。各リクエストは独立しているので、「前の画像のポスターを大きく」と書くだけでは直前の画像は引き継がれません。基準にする画像と、今回の変更指示を毎回送ります。

元画像と採用した各段階を別名で保存しておけば、変更が意図から外れたときに戻れます。たとえば採用済みの `room-v1.png` を入力にして「ポスターを少し大きくし、額縁の反射を弱める」と指示し、結果は `room-v2.png` に保存します。新しい結果を開いて採用を決めるまでは、前の画像を上書きしない運用にすると比較しやすくなります。

### 会話を管理するならResponsesを検討する

一度画像を受け取り、編集してファイルを返す機能なら、ここまでの `images.edit()` で実装を始められます。前の出力について「ポスターをもう少し大きく」「光沢を弱く」と会話を続けたり、別のツールと組み合わせたりする場合は、Responses APIの画像生成ツールが適しています。[画像生成ガイド](https://developers.openai.com/api/docs/guides/image-generation)

Responsesでは、対応する会話用モデルを最上位の `model` に指定し、`image_generation` ツールを使います。`gpt-image-2` をそのままResponsesの最上位モデルへ移す方式ではありません。会話やファイルIDを使う流れも、この仕組みに合わせて設計します。

過去の `dall-e-2` エラーを避けることだけを理由にAPIを変更すると、元の送信内容や接続先の問題が分からないままになります。まず直接編集の最小リクエストを確認し、複数ターンの編集が必要になった段階でResponsesを選ぶと判断しやすくなります。

生成も含めたモデルの使い方は、[GPT Image 2 APIの日本語ガイド](/ja/topics/gpt-image)で確認できます。今回の編集では、最小リクエスト、画像保存、マスク追加の順に進めると、次に直すべき箇所を見失わずに済みます。
