Nano Banana 2 Thought Signatureエラー:2ターン目の400の直し方
Nano Banana 2の2ターン目の400は、前の画像を思考シグネチャなしで送り直したのが原因です。previous_interaction_idかSDKのチャットで直ります。
目次

Nano Banana 2(gemini-3.1-flash-image)で1枚目の画像は生成できたのに、続けて編集を頼んだ2ターン目で400 INVALID_ARGUMENTと「Image part is missing a thought_signature in content position 2, part position 1」が返る場合、原因は前のターンでモデルが返した画像パーツを、思考シグネチャ(thought_signature)を付けずに送り直していることです。会話履歴を「テキストと画像データだけ」の形で組み直すと、この署名が落ちます。
直し方は次の順に検討してください。
- Interactions APIを使う:次のターンに
previous_interaction_idを渡せば、署名はサーバー側で保持されます。Googleの画像生成ガイドの複数ターン編集もこの書き方です。 - generateContentを続けて使う:SDKのチャット機能を使うか、応答の
contentを加工せずにそのまま履歴へ追加します。 - フレームワーク経由で呼んでいる:署名を保持する版に更新します。Vercel AI SDKは
ai6.0.114で画像の署名に対応しました。
署名の文字列を自分で用意したり差し替えたりする方法は、画像パーツについてはGoogleの文書に記載がありません。
missing a thought_signatureの原因:前の画像の署名が落ちている
思考シグネチャは、モデルの内部推論を暗号化した値です。GoogleのGemini APIの思考ガイドは、複数ターンのやり取りで推論の連続性を保つために必要なものと説明しています(2026年10月5日時点)。エラー文に出てくる/thought-signaturesのページは、現在この思考ガイドに転送されます。
generateContent APIには専用の思考ブロックがなく、署名は応答のパーツに付くメタデータとして返ります。付く場所はfunctionCallパーツや応答の最後のパーツなどで、REST上のフィールド名はthoughtSignature、Python SDKではpart.thought_signatureです。エラー文が「Image part」と名指ししているとおり、Nano Banana 2は画像パーツの署名が欠けているとリクエストを拒否します。
2ターン目で起きやすいのは次のような流れです。
- 1ターン目でモデルが画像を返す。その画像パーツには署名が付いている。
- アプリやフレームワークが、表示用に画像のバイト列とテキストだけを取り出して履歴に保存する。
- 2ターン目のリクエストで、その履歴からモデルの発言を組み直して送る。画像はあるが署名がない。
- APIが
INVALID_ARGUMENTで拒否する。
Vercel AI SDKのissue #12988(2026年3月)がまさにこの例です。gemini-3.1-flash-image-previewで画像を生成した次のターンで、SDKが前の画像を署名なしで送り直し、上と同じ「Image part is missing a thought_signature in content position 2, part position 1」が返っていました。報告者は「同じ流れはgemini-2.5-flash-imageでは問題なく動いていた」と書いています。エラー文の位置番号は、署名が欠けたパーツがリクエストのどこにあるかを指しています。
同じ問題は、呼び出し方によって次の名前でも現れます。
| 表示 | 出る場所 | 意味 |
|---|---|---|
Image part is missing a thought_signature… | generateContentのHTTP 400 INVALID_ARGUMENT | 履歴内の画像パーツに署名がない |
MISSING_THOUGHT_SIGNATURE | generateContentのfinishReason | リクエスト内で少なくとも1つの署名が欠けている |
missing_thought_signature | Interactions APIのエラーコード | 必要な思考シグネチャがない |
finishReasonとエラーコードの説明は、GoogleのgenerateContentリファレンスとAPIエラー一覧によります(2026年10月5日時点)。
Nano Banana 2とNano Banana Proの違い:Proで動いたコードが400になる
同じコードがNano Banana Pro(gemini-3-pro-image)では動き、Nano Banana 2に替えると400になることがあります。これは署名を検証する厳しさがモデルで違うためです。
Google Cloudの思考シグネチャのページによると、Gemini 3モデルは以前のGeminiより署名の検証が厳しく、必要な署名が返されなければ400エラーになります。一方でGemini 3 Pro Imageは検証を強制せず、署名を返さなくても400にはなりません。ただし同じページは、会話の文脈を保つためにPro Imageでも署名を返すべきだとしています。また、2025年12月のDifyプラグインの報告では、Nano Banana Proのプレビュー版でも同じ「Image part is missing a thought_signature」の400が出ています。この例外を当てにせず、どちらのモデルでも署名を返してください。
Nano Banana 2の挙動はこのページに書かれていません。前節の開発者報告では、Nano Banana 2は画像パーツの署名がないと400を返しています。つまり、Proで署名を捨てていたコードは、エラーが出なかっただけで不完全だったことになります。どのモデルでも、モデルが返したパーツは署名ごと返す前提で組んでおくと、モデルを切り替えても壊れません。

モデルIDも確認してください。gemini-3.1-flash-image-previewとgemini-3-pro-image-previewは2026年6月25日に、初代Nano Bananaのgemini-2.5-flash-imageは2026年10月2日に提供が終了しています(Gemini APIの非推奨ページ)。古い記事やissueのコードをそのまま使う場合は、先に現行のgemini-3.1-flash-image(Nano Banana 2)かgemini-3-pro-image(Nano Banana Pro)へ置き換えます。モデルごとの基本の呼び出し方はGemini画像生成APIの使い方:無料枠なし・1枚$0.0336〜の3モデルにまとめています。
直し方1:Interactions APIのprevious_interaction_idを渡す
新しく書くコードなら、Interactions APIのステートフルモードが最も手間がかかりません。思考ガイドによると、store: trueの状態で次のターンにprevious_interaction_idを渡すと、思考ブロックと署名を含む会話の状態をサーバーが管理するため、署名について何もする必要がありません。Googleの画像生成ガイドの複数ターン編集の例も、storeを指定せずにprevious_interaction_idだけを渡しています。
次のコードは、Googleの画像生成ガイドの形に合わせた例です。実行はしていないので、SDKのバージョンによって属性名が違わないか確認してから使ってください。
import base64
from google import genai
client = genai.Client() # 環境変数のAPIキーを使う
MODEL = "gemini-3.1-flash-image"
# 1ターン目:画像を生成
first = client.interactions.create(
model=MODEL,
input="木のテーブルに置いた白いマグカップの商品写真を作ってください",
)
with open("turn1.png", "wb") as f:
f.write(base64.b64decode(first.output_image.data))
# 2ターン目:前のやり取りのIDを渡すだけ。署名はサーバー側で引き継がれる
second = client.interactions.create(
model=MODEL,
input="マグカップの色だけを紺色に変えてください。ほかは変えないでください",
previous_interaction_id=first.id,
)
if second.output_image:
with open("turn2.png", "wb") as f:
f.write(base64.b64decode(second.output_image.data))会話の状態を自分で持つステートレスモードで使う場合は、モデルから受け取った思考ブロックをすべて、受け取ったとおりに送り返す必要があります。削除や書き換えは不可で、Google検索などの組み込みツールの呼び出しと結果に付いた署名も同様です。
切り替え時の注意として、Interactions APIではセーフティ設定のカスタマイズが使えません。generateContentでセーフティ設定を変えている場合は、次の直し方2で署名を保持するほうが影響が小さく済みます。
直し方2:generateContentはSDKのチャットか応答contentをそのまま履歴へ
generateContentを使い続けるなら、Google Cloudのページにあるとおり、公式のGoogle Gen AI SDK(Python、Node.js、Go、Java)でチャット履歴の機能を使うか、前の応答のcontentをまるごと履歴に追加すれば、署名は自動で引き継がれます。
チャット機能を使う場合の例です(未実行)。
from google import genai
from google.genai import types
client = genai.Client()
chat = client.chats.create(
model="gemini-3.1-flash-image",
config=types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"]),
)
def save_images(response, prefix):
for i, part in enumerate(response.candidates[0].content.parts):
if part.inline_data:
with open(f"{prefix}-{i}.png", "wb") as f:
f.write(part.inline_data.data)
r1 = chat.send_message("木のテーブルに置いた白いマグカップの商品写真を作ってください")
save_images(r1, "turn1")
# 署名付きの前ターンはchatオブジェクトが保持している
r2 = chat.send_message("マグカップの色だけを紺色に変えてください。ほかは変えないでください")
save_images(r2, "turn2")履歴を自分で組み立てる場合は、応答のcontentオブジェクトをそのまま追加します。パーツを1つずつ取り出して新しく作り直すのが、署名が落ちる典型的な書き方です。
config = types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"])
contents = [types.Content(role="user", parts=[types.Part(text="白いマグカップの商品写真を作ってください")])]
r1 = client.models.generate_content(model="gemini-3.1-flash-image", contents=contents, config=config)
contents.append(r1.candidates[0].content) # 署名を含むモデルの発言をそのまま追加
contents.append(types.Content(role="user", parts=[types.Part(text="色だけを紺色に変えてください")]))
r2 = client.models.generate_content(model="gemini-3.1-flash-image", contents=contents, config=config)RESTで直接呼ぶ場合も考え方は同じです。モデルの発言に含まれていたパーツを、thoughtSignatureを付けたまま同じパーツの中で返します。
{
"role": "model",
"parts": [
{
"inlineData": { "mimeType": "image/png", "data": "IMAGE_BASE64" },
"thoughtSignature": "SIGNATURE_FROM_RESPONSE"
}
]
}どのパーツに署名が付くかは応答によって変わるので、決め打ちせず、受け取ったパーツをすべてそのままの順番で返すのが確実です。Google Cloudのページは、さらに次の点を挙げています。
- 署名は元のパーツの中に入れたまま返す。
- 署名のあるパーツと、ないパーツを1つにまとめない。
- 署名を持つパーツ同士もまとめない(署名の文字列は結合できない)。
- ストリーミングでは、署名が空のテキストを持つパーツで届くことがある。
finish_reasonが返るまですべてのパーツを確認する。
最後の点は、chunk.textが空のチャンクを読み飛ばす処理で起こりがちな取りこぼしです。

直し方3:Vercel AI SDKやDify、n8n経由ならライブラリを更新する
自分のコードで履歴を組み立てていない場合、署名を落としているのは間に入るライブラリやツールです。この場合はアプリ側の書き方を変えても直らないので、まず更新を確認します。
- Vercel AI SDK:issue #12988の修正(画像の思考シグネチャへの対応)は
ai6.0.114で公開されています。それより前の版で2ターン目が失敗するなら、更新が最初の対処です。 - Difyやn8n:DifyのGeminiプラグインやn8nでも、同種のエラーの報告があります。プラグインやノードを最新にして再現するか確かめ、直らなければモデルID、エラー文、何ターン目で失敗するかを添えて報告すると原因を特定してもらいやすくなります。
修正を待てない場合は、画像を続けて編集する部分だけをツールの外に出し、直し方1のInteractions APIを直接呼ぶ形にするのが確実です。履歴の作り直しをツールに任せる限り、署名が保たれる保証はありません。
署名の欄に固定の文字列を入れて検証を通す方法について、Googleの文書にあるのは次節の関数呼び出し用の値だけで、画像パーツに使えるという記載はありません。
関数呼び出しの400:最初のfunctionCallと並列呼び出しの順番
画像編集ではなく、Gemini 3モデルのツール呼び出しやエージェント処理で思考シグネチャの400が出る場合は、関数呼び出しの規則を確認します。Google Cloudのページが挙げる規則は次のとおりです。
- 応答に
functionCallパーツがあれば、署名が必須です。並列呼び出しでは最初のfunctionCallパーツだけに、順次呼び出しでは各ステップのfunctionCallパーツに署名が付きます。 - 次のリクエストには、
functionCallパーツを署名ごと、返されたとおりに含めます。現在のターンのすべてのステップが検証され、どこかのステップで最初のfunctionCallの署名が欠けると400になります。ターンは、functionResponseではない最新のユーザーメッセージから始まります。 - 並列呼び出しでは、すべての呼び出しを並べたあとに、すべての結果を並べます(FC1+署名、FC2、FR1、FR2)。呼び出しと結果を交互に並べる(FC1+署名、FR1、FC2、FR2)と400になります。
functionCallを含まない応答では、最後のパーツに署名が付くことがあります。これを返すことは推奨されますが、省略してもエラーにはなりません。
Google CloudのChat Completions(OpenAI互換)形式では、署名はtool_callsの各要素のextra_content.google.thought_signatureに入ります。ここもほかの欄と同じく、受け取った値をそのまま返します。
APIが生成していないfunctionCallパーツを履歴に入れる必要がある場合、例えば署名を持たない別のモデルの履歴を移してくる場合だけは、thought_signatureにskip_thought_signature_validatorを設定できます。ただしGoogleはこれを「最後の手段」とし、モデルの性能を下げると明記しています。対象はfunctionCallパーツで、画像パーツの回避策ではありません。
なお、Interactions APIを自分で状態管理して使う場合、会話の途中でモデルを切り替えても、前のモデルの思考ブロックはそのまま送り返すよう思考ガイドに書かれています。互換性はバックエンド側で処理されます。
思考シグネチャの疑問:思考レベル、保存、Corruptedの表示
思考レベルを最小にしていても、署名を返す必要はありますか。
あります。思考ガイドでは、思考ステップの署名は、モデルの推論がごくわずかな場合でも必ず含まれるとされています。Google Cloudのページも、MINIMALの思考レベルでも前の応答の署名を返す必要があるとしています。思考を絞っても、署名の処理は省けません。
会話をデータベースに保存して、あとで編集を再開するにはどうしますか。
generateContentなら、テキストや画像だけでなく、署名を含むモデルの発言を丸ごと保存します。署名はRESTでは文字列(bytes形式)、Python SDKではバイト列なので、保存と読み出しで値が変わらない形にしてください。Interactions APIのステートフルモードなら、保存するのは前のやり取りのidで、再開時にそれをprevious_interaction_idとして渡します。
「Corrupted thought signature」や「Thought signature is not valid」と表示されたら何を見ますか。
署名が欠けているのではなく、届いた値が受け取ったときと違う可能性を先に疑います。Googleの文書が禁じているのは、署名の書き換え、署名付きパーツと署名なしパーツの結合、署名付きパーツ同士の結合です。保存時の文字コードやbase64の変換、パーツの並べ替えや結合をしていないかを確認してください。
Geminiアプリで画像が出ないときも、この署名が関係していますか。
関係しません。思考シグネチャはAPIで会話履歴を自分たちで送るときの問題です。アプリでの不具合はGeminiでNano Bananaが動かない時の12個の直し方やNano Banana Proエラー5の対処法、APIが200を返したのに画像がない場合はNano Banana APIで画像が生成されない原因と対処、429など署名以外のAPIエラーはGemini画像生成エラーの直し方を参照してください。
公式ドキュメント
リクエストパラメータ、料金、課金ルールは LaoZhang API の開発者ドキュメントを参照してください:画像生成の課金と失敗処理。
注記:laozhang.ai は当ブログ運営チームの自社サービスです。その料金は中立的な比較ではなく、自社の提示価格です。
参考資料6
本文で参照している外部ページを、登場順に並べています。最終更新日:2026年10月5日。
参考資料6
本文で参照している外部ページを、登場順に並べています。最終更新日:2026年10月5日。
- 1.Gemini APIの思考ガイドai.google.dev/gemini-api/docs/thinking
- 2.generateContentリファレンスai.google.dev/api/generate-content
- 3.APIエラー一覧ai.google.dev/gemini-api/docs/api-errors
- 4.思考シグネチャのページdocs.cloud.google.com/gemini-enterprise-agent-platform/models/thinking/thought-signatures
- 5.Difyプラグインの報告github.com/langgenius/dify-official-plugins/issues/2262
- 6.Gemini APIの非推奨ページai.google.dev/gemini-api/docs/deprecations





