google-genai を 2.30.1 に上げた手元の環境で、config の綴りをわざと一字だけ崩して呼び出してみました。temperature を temprature と書くだけの、よくある打ち間違いです。
返ってきたのは、認証エラーでも通信エラーでもありませんでした。ダミーのキーで呼んでいるのに、リクエストが出る前に pydantic.ValidationError で止まったのです。
最初にお伝えしたいのは、この赤いエラーは不具合ではなく、SDK からの親切な指摘だということです。原因の切り分けは、エラー文に出ている名前を SDK 側の定義と見比べるだけで終わります。
出るエラーの全文と、どこで出るか
GenerateContentConfig に存在しない名前を渡すと、次のエラーになります。
1 validation error for GenerateContentConfig
max_output_token
Extra inputs are not permitted [type=extra_forbidden, input_value=256, input_type=int]2 行目の名前(ここでは max_output_token)が、SDK の知らないキーです。末尾の s が一つ足りないだけでした。
dict をそのまま config= に渡す書き方でも同じです。ダミーキーで client.models.generate_content(...) を呼んだところ、通信に入る前に同じ extra_forbidden が上がりました。つまり、課金もクォータも消費する前に止まります。
個人開発で小さなスクリプトを書くとき、私は設定を dict に持たせる書き方をよく使います。dict は IDE の補完が効かないぶん、綴りの確認が自分の目頼みになります。
原因は「未定義の名前は受け付けない」という設計
GenerateContentConfig は、定義されていないキーを黙って捨てずに拒否します。手元で確かめた範囲では、次の挙動でした。
| 渡した名前 | 結果 | 正しい名前 |
|---|---|---|
max_output_token | extra_forbidden で停止 | max_output_tokens |
temprature | extra_forbidden で停止 | temperature |
system_instructions | extra_forbidden で停止 | system_instruction |
stop_sequence | extra_forbidden で停止 | stop_sequences |
maxOutputTokens(キャメルケース) | 通る(値は max_output_tokens に入る) | そのままで可 |
見落としやすいのは最後の行です。キャメルケースの別名は通るので、「ケースの違いは許されるのに、綴りの違いは許されない」という線引きになっています。コピーしたキー名は通るのに、自分で打ったキー名だけが落ちる、という現象はここから生まれるのだと思います。
呼び出す前に、怪しいキーを洗う
綴りを目で追う代わりに、SDK が知っているキー一覧と突き合わせます。difflib で近い候補も出しておくと、直す先がすぐ分かります。
import difflib
from google.genai import types
def check_config(cfg: dict) -> list[str]:
"""config(dict) のうち SDK が知らないキーと、近い候補を返す。"""
fields = types.GenerateContentConfig.model_fields
known = set(fields)
# キャメルケースの別名(maxOutputTokens など)は通るので許可する
aliases = {f.alias for f in fields.values() if f.alias}
problems = []
for key in cfg:
if key in known | aliases:
continue
near = difflib.get_close_matches(key, sorted(known), n=1, cutoff=0.6)
hint = f"(もしかして {near[0]!r})" if near else ""
problems.append(f"{key!r} は未定義{hint}")
return problems
cfg = {"temprature": 0.1, "system_instructions": "短く答える", "stop_sequence": ["END"]}
print(check_config(cfg) or "OK")手元で実行すると、次のように返りました。
["'temprature' は未定義(もしかして 'temperature')", "'system_instructions' は未定義(もしかして 'system_instruction')", "'stop_sequence' は未定義(もしかして 'stop_sequences')"]3 つの綴りミスを、一度の実行でまとめて拾えました。pydantic のエラー文にも複数件は並びますが、近い候補までは教えてくれません。候補つきで先に洗っておくほうが、直す手が止まりません。
なぜ model_fields を使うかというと、SDK のバージョンが上がってフィールドが増えても、一覧を自分で保守しなくて済むからです。手元の 2.30.1 では 37 個のフィールドが定義されていました。
もう一つの症状は、赤くならずに text が None になる
綴りミスは赤いエラーで止まってくれるぶん、まだ良いほうです。紛らわしいのは、エラーも出ないのに response.text が None になる場合です。
私が確かめたのは、実際の API 応答ではなく、SDK の型で組み立てた応答に対する SDK の挙動です。同じ None でも、理由は三通りに分かれました。
| 応答の状態 | text | 見るべき場所 |
|---|---|---|
| パーツが関数呼び出しだけ | None(警告ログも出る) | response.function_calls |
| finish_reason が MAX_TOKENS でパーツなし | None | finish_reason と出力トークンの上限 |
| candidates が空で block_reason が付く | None | prompt_feedback.block_reason |
関数呼び出しだけの場合は、non-text parts in the response: ['function_call'] という警告がログに出ます。ここで「text が空だ」と読み替えず、function_calls を見に行くのが正解です。
三通りを一行で言い分ける関数を置いておきます。
def explain_empty(resp) -> str:
"""text が None のとき、原因を一行で返す。"""
if not resp.candidates:
fb = resp.prompt_feedback
return f"candidates なし / block_reason={getattr(fb, 'block_reason', None)}"
c = resp.candidates[0]
parts = (c.content.parts if c.content else None) or []
kinds = [k for p in parts
for k in ("function_call", "thought", "inline_data", "executable_code")
if getattr(p, k, None)]
return f"finish_reason={c.finish_reason.name} / parts={len(parts)} / 非テキスト={kinds or 'なし'}"先ほどの三つの状態に対する出力は、次のとおりでした。
tool -> finish_reason=STOP / parts=1 / 非テキスト=['function_call']
max -> finish_reason=MAX_TOKENS / parts=0 / 非テキスト=なし
block -> candidates なし / block_reason=SAFETYそれでも直らないときの次の一手
エラー文の名前を直しても同じ場所で落ちる場合は、確かめる順番を決めておくと迷いません。
pip show google-genaiで SDK のバージョンを確かめます。フィールド名は版で変わることがあるので、エラーを見たマシンと同じ版でmodel_fieldsを引いてください- dict をやめて
types.GenerateContentConfig(...)に書き換えます。IDE の補完が効くので、綴りミスはエディタの段階で見つかります - ラッパー(フレームワーク側の設定オブジェクト)を挟んでいる場合は、SDK へ渡る直前の dict を一度
printします
止まってくれる綴りミスは親切で、止まらない None のほうが高くつきます。 設定の名前は SDK に決めてもらい、人間は一致しているかだけを確かめる、という分担にしています。
まずは今日、自分のコードの config を check_config に一度通してみていただければと思います。構造化出力が本番で静かにスキーマから外れていく場面まで計測したい方には、運用メモに検証と再試行の置き方を書き残してあります。