Gemini の構造化出力が MAX_TOKENS と 400 で失敗し続けた:thinking でも音声形式でもなく、response_schema が原因だった

Gemini API で、採点が MAX_TOKENS で失敗し続け、音声の文字起こしが 400 INVALID_ARGUMENT で失敗し続けました。どちらも最初の見立ては外れ、本当の原因は response_schema(JSON の出力の形の指定)でした。2026年9月13日と14日に測ったことと、切り分けの手順を書きます。

Gemini の response_schema が MAX_TOKENS と 400 の2つの失敗を起こす流れの札

Gemini API で JSON を返させる時、MAX_TOKENS で止まり続けたり、400 INVALID_ARGUMENT だけが返ったりしたら、response_schema(JSON の出力の形の指定)を疑ってください。私たちは、MAX_TOKENS は「thinking が出力の枠を使っている」と、400 は「音声の形式が読めない」と考えて、どちらも外しました。本当の原因は、前者は上限の無い文字列や配列でのループ、後者は maxItems の値でした。

接客の録音アプリ(TalkLens)では、録音の文字起こしと採点に Gemini を使っています。2026年9月13日と14日に、2つの失敗が続けて起きました。どちらも、最初の見立てが間違っていて、間違いに気づくのに時間がかかりました。この記事は、その記録です。モデルの名前や上限の数字は、その時点で測った値で、いまも同じとは限りません。お客様の文や、プロンプトの全文は書きません。

失敗1:採点が MAX_TOKENS で、2回続けて失敗した

本番で、採点が2回続けて MAX_TOKENS(出力の枠を使い切って止まること)で失敗しました。1回あたり約90秒かかったうえでの失敗でした。

Gemini の採点が MAX_TOKENS で2回続けて失敗し、thinking が出力の枠を使っていると考えて枠を8192から16384、24576へ広げたが失敗は続いた
最初の見立ては、枠が足りないというものだった

測ってみると、thinking は原因ではなかった

同じ文字起こし(2,461文字、有効な項目30個)で、thinking の量を測りました。

同じ文字起こしで thinking の量を測った結果。low と minimal では0、既定の medium でも2,534で、24,576の枠を使い切る量には足りなかった
thinking は、原因ではなかった
thinking の設定 thinking に使われた量(thoughtsTokenCount)
low 0
minimal 0
指定しない(既定は medium) 2,534

既定の設定でも2,534で、24,576の枠を使い切る量には、まったく足りません。thinking は原因ではありませんでした。

本当の原因は、出力のループだった

本当の原因は、構造化出力(response_schema)のデコーダが、同じ言葉の繰り返しに入り込み、出力の上限まで走り続けることでした。

白い螺旋階段を真上から見た写真。同じところをぐるぐる回る様子を思わせる
本当の原因は、出力が同じところを回り続けたことだった(イメージ写真)
response_schema に上限の無い文字列や配列があると、構造化出力のデコーダが同じ言葉の繰り返しに入り、JSON を閉じる部分が選ばれず、出力の上限まで走って MAX_TOKENS になる
枠を広げても、失敗までの時間が延びるだけ

直し方:長さの上限を付け、出力の枠は逆に下げた

直し方。response_schema の文字列に maxLength、配列に minItems と maxItems、各階層に propertyOrdering を付け、出力の枠は24576から8192に下げ、失敗時に usageMetadata を記録する
ループが成り立たない形にして、枠は保険にする

直したあと、実データで3回続けて成功し、30項目すべてが毎回返りました。

失敗2:音声の文字起こしが、400 INVALID_ARGUMENT で失敗した

別の失敗は、音声の文字起こしでした。Gemini が 400 INVALID_ARGUMENT を返し続けました。

音声の文字起こしが 400 INVALID_ARGUMENT で失敗し続け、m4a や CAF が読めないと考えて mp3 に変換したが、400は続いた
最初の見立ては、音声の形式だった

切り分け:同じ音声を1回だけ上げ、リクエストの組み立てだけを変えた

形式を疑うのをやめ、送る側の組み立てを1つずつ変えて確かめました。

400の切り分けの結果。稼働中の実装と同じ形は200、responseMimeType を足しても200、responseSchema を足すと400、スキーマの要素では maxItems だけが400
1つずつ足して、400になる瞬間を探す
  1. 同じ音声を、1回だけアップロードする

    音声は固定します。変えるのは、リクエストの組み立てだけです。

  2. すでに動いている実装と同じ形で送る

    すでに稼働していた実装(259件の実績)と同じ形で送ると、200が返りました。

  3. 1つずつ足していく

    responseMimeType を足しても200。responseSchema を足した瞬間に、400になりました。

  4. スキーマの中身を、1つずつ足す

    enum、maxLength、propertyOrdering は、すべて200。maxItems だけが 400 でした。

音声は、最初から無関係でした。音声を使わない、テキストだけの呼び出しでも、同じスキーマなら400になりました。

maxItems の上限は、決まった数ではなかった

さらに調べると、maxItems には上限の値があり、その値は決まった数ではなく、スキーマ全体の大きさで変わりました。二分探索で測ると、次のとおりでした。

黄色い背景に置かれた、丸まった巻き尺の写真
長さの上限を、はっきり決めた(イメージ写真)
maxItems の上限を二分探索で測った結果。発言の区切りの配列は126まで、単純な文字列の配列は1000で400、採点のスキーマは項目が50個まで。上限はスキーマ全体の大きさで変わる
2026年9月14日に測った値。Gemini は理由を返さない
スキーマ 通った最大の maxItems
文字起こしの発言の区切りの配列 126(127から400)
単純な文字列の配列 200台まで通り、1000で400
採点のスキーマ 項目が50個まで

直し方と、前の結論の訂正

maxItems を外して形式をもう一度確かめると、m4a は audio/mp4・audio/m4a・audio/aac のどれで申告しても読めた。CAF は200が返るが録音に無い台詞が出力された
「m4a は読めない」という前の結論は、間違いだった

maxItems を外して形式をもう一度確かめると、「Gemini は m4a と CAF を読めない」という前の結論も、間違いでした。m4a は、audio/mp4、audio/m4a、audio/aac のどれで申告しても、正しく読めました。

まとめ

Gemini の構造化出力が MAX_TOKENS と 400 で失敗し続けた:thinking でも音声形式でもなく、response_schema が原因だった:要点のまとめ。MAX_TOKENS が続く時、thinking を疑う前に、thoughtsTokenCount を測る。今回は原因ではなかった
この記事の要点
  • MAX_TOKENS が続く時、thinking を疑う前に、thoughtsTokenCount を測る。今回は原因ではなかった
  • 原因は、response_schema の上限の無い文字列や配列でのループ。maxLength などの上限を付け、出力の枠は下げる
  • 400 INVALID_ARGUMENT は、同じ入力のまま、リクエストの組み立てを1つずつ変えて切り分ける
  • maxItems の上限は決まった数ではなく、スキーマ全体の大きさで変わる。Gemini は理由を返さない
  • 最初の見立ては、2回とも間違っていた。間違いは記録の中で訂正して、次の判断に活かす

この記事は、AI(Claude)の支援を受けて作成し、筆者が内容を確認しています。

  • Gemini
  • response_schema
  • MAX_TOKENS
  • 400 INVALID_ARGUMENT
  • maxItems
  • 切り分け

関連する記事

この記事に関連するサービス・アプリ

ブログの一覧へ