数日前まで安定して動いていた Gemini API のバックエンドが、ある日の朝から突然 DEADLINE_EXCEEDED を返し始める――。私自身、運営しているサービスでこの症状に遭遇して、原因切り分けに半日溶かしたことがあります。レート制限のように分かりやすいエラーメッセージは出ず、ただ「期限切れ」とだけ告げられる種類のエラーなので、最初の一手で迷うとそのまま深い泥沼に入りがちです。
ここでは私が自分のサービスで実際に効いた診断順序を、副作用の少ない順に5つのポイントとしてまとめています。コードを大きく書き換える前に、まずはこの順番で確認していただくと、原因がほぼ特定できるはずです。
エラーの正体 — どこで「期限切れ」になっているのか
DEADLINE_EXCEEDED は本来 gRPC 由来のエラーコードで、「指定した時間以内にレスポンスが返ってこなかった」ことを意味します。Gemini API では、Python SDK(google-genai)が gRPC を使う場合と、Node.js SDK や REST API が HTTPS を使う場合で、エラーの見え方が少し違います。
- Python
google-genai(gRPC)の場合:google.api_core.exceptions.DeadlineExceeded: 504 Deadline Exceeded - Node.js SDK / fetch の場合:
Error: Request timed outあるいはAbortErrorとして現れる - AI Studio から実行している場合: 「The request timed out」という UI メッセージで止まる
ここで大切なのは、「クライアント側のタイムアウトが切れた」のと「サーバー側で処理が完了しなかった」のは別物だということです。クライアント側のタイムアウトであれば設定値を伸ばせば解決しますが、サーバー側の処理時間が伸びている場合は、入力サイズやモデル選択を見直す必要があります。最初の一手は、この切り分けから始めるのが近道です。
ポイント1: 自分のコードを疑う前に「サーバー側の処理時間」を測る
私が最初にやることは、まったく同じプロンプト・同じモデルを Google AI Studio で実行してみることです。AI Studio は内部でかなり長いタイムアウトを持っているので、サーバー側の実時間がそのまま見えます。
たとえば自分のコードで 60 秒タイムアウトを設定していて、AI Studio では 90 秒かかって正常に返ってくるなら、原因は「クライアント側のタイムアウト不足」です。逆に AI Studio でも止まる、あるいは極端に遅い場合は、入力サイズかモデル選択に問題があります。
ローカルでも処理時間を測れるように、最初に簡単な計測ラッパーを入れておくと診断が一気に楽になります。
# 診断用: Gemini API 呼び出しの所要時間を測るラッパー
import time
from google import genai
client = genai.Client(api_key="YOUR_API_KEY")
def measured_generate(model: str, contents):
start = time.perf_counter()
try:
response = client.models.generate_content(model=model, contents=contents)
elapsed = time.perf_counter() - start
print(f"[OK] {model} took {elapsed:.2f}s")
return response
except Exception as e:
elapsed = time.perf_counter() - start
print(f"[ERR] {model} failed at {elapsed:.2f}s: {type(e).__name__}: {e}")
raise
# 期待出力(正常時):
# [OK] gemini-2.5-flash took 3.42sこのログを 10 〜 20 リクエスト分眺めるだけで、「平均は 5 秒程度なのに、ときどき 60 秒に張り付いて切れる」といった分布が見えてきます。
ポイント2: 入力サイズ(特に長文・PDF・動画)のしきい値を疑う
DEADLINE_EXCEEDED がランダムに発生しているように見えても、よく観察すると「特定のリクエストだけが遅い」ことが多いです。私の経験上、以下の3パターンが頻出します。
- 長文プロンプト: コンテキスト 30 万トークンを超えたあたりから処理時間が伸び始める
- PDF/Office ドキュメント: ページ数が多いほど内部の解析時間が増える
- 動画: 数分以上の動画は処理時間が大きく揺れる
長文を1リクエストで送る設計は、運用が安定するまで避けたほうが無難です。代わりに「事前に File API へアップロードしておき、推論時はファイル参照だけ渡す」設計にすると、転送時間と解析時間が分離できて見通しが良くなります。
# Before: PDF を毎回 inline で送る(遅延が大きい)
with open("report.pdf", "rb") as f:
pdf_bytes = f.read()
response = client.models.generate_content(
model="gemini-2.5-pro",
contents=[{"inline_data": {"mime_type": "application/pdf", "data": pdf_bytes}},
"この資料の要点を5つに絞って教えてください。"]
)
# After: File API に上げておいて、推論時はファイル参照だけ渡す
uploaded = client.files.upload(file="report.pdf")
response = client.models.generate_content(
model="gemini-2.5-pro",
contents=[uploaded, "この資料の要点を5つに絞って教えてください。"]
)
# 期待出力: 同じ PDF でも 2 回目以降の推論が大幅に高速化されるリクエストごとの所要時間が安定するだけでなく、同じファイルを複数回参照するときに転送料を節約できる副次的なメリットもあります。
ポイント3: SDK 既定のタイムアウトを「現実的な値」に上書きする
意外と見落とされがちなのが、SDK のデフォルトタイムアウトです。google-genai Python SDK は HTTP オプションでタイムアウトを上書きできます。Pro 系モデルで複雑な推論をかけるなら、120〜180 秒くらいまで伸ばしておくのが安全です。
from google import genai
from google.genai import types
client = genai.Client(
api_key="YOUR_API_KEY",
http_options=types.HttpOptions(timeout=180_000), # ミリ秒指定 (180s)
)
response = client.models.generate_content(
model="gemini-2.5-pro",
contents="長めの分析レポートを書いてください。",
)
# 期待出力: 60 秒以上かかる推論でもクライアント側で切れなくなるNode.js / fetch を直接使っている場合は、AbortController を使って明示的にタイムアウトを管理します。フレームワークに任せると、Cloudflare Workers や Vercel の Edge Runtime でデフォルトが短すぎることがあるので、自前で持つのが安全です。
// Node.js / Edge Runtime 共通: AbortController でタイムアウトを管理
const ctrl = new AbortController();
const timeout = setTimeout(() => ctrl.abort(), 180_000); // 180s
try {
const res = await fetch(
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-goog-api-key": process.env.GEMINI_API_KEY!,
},
body: JSON.stringify({ contents: [{ parts: [{ text: "..." }] }] }),
signal: ctrl.signal,
}
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// 期待出力: data.candidates[0].content.parts[0].text に応答が入る
} finally {
clearTimeout(timeout);
}ただし「タイムアウトを伸ばす」は症状を遅らせるだけの対処になりがちです。あわせて Gemini API のレート制限とクォータを本番運用で管理する完全ガイド で紹介している同時実行数の制御を入れて、サーバー側に負荷を集中させない設計にすると、根本的に安定します。
ポイント4: モデル選択を見直す(Pro と Flash の使い分け)
gemini-2.5-pro の Thinking 系モデルは、内部で長い推論ステップを回すため、本質的に応答時間が伸びます。Flash 系・Flash-Lite 系で十分なタスクまで Pro に投げているとタイムアウト頻度が増えるため、ワークロードに合わせた使い分けが効きます。
私が運用しているサービスでは、ざっくり以下のように振り分けています。
- 短い要約・分類・抽出:
gemini-2.5-flash-lite - 通常の文章生成・FAQ 応答:
gemini-2.5-flash - 長文の構造化・複雑な推論・コード生成:
gemini-2.5-pro
すべてを Pro でこなそうとすると、平均応答時間が伸びるだけでなくコストも跳ね上がります。実際にプロダクションで観測した範囲では、Flash で十分なタスクまで Pro で投げると、応答時間が 2〜3 倍、料金が 5〜10 倍になることもありました。タイムアウトに悩んでいるなら、まずは「このリクエストは本当に Pro が必要か」を見直すのが、効果が出るまでの時間が一番短い改善策です。
ポイント5: リトライ戦略を「指数バックオフ + ジッタ」で実装する
DEADLINE_EXCEEDED を見るたびに即座に再試行するナイーブなリトライは、サーバー側の混雑をさらに悪化させて、結果的に DEADLINE_EXCEEDED を増やすことがあります。リトライは必ず指数バックオフ(待ち時間を徐々に伸ばす)と、ジッタ(小さなランダム揺れ)を組み合わせて実装してください。
import random
import time
from google import genai
from google.api_core.exceptions import DeadlineExceeded, ServiceUnavailable
client = genai.Client(api_key="YOUR_API_KEY")
def generate_with_retry(model: str, contents, max_retries: int = 3):
for attempt in range(max_retries):
try:
return client.models.generate_content(model=model, contents=contents)
except (DeadlineExceeded, ServiceUnavailable) as e:
if attempt == max_retries - 1:
raise
# 指数バックオフ + ジッタ (4s, 8s, 16s + 0〜1s 揺らぎ)
wait = (2 ** (attempt + 2)) + random.random()
print(f"[retry {attempt + 1}/{max_retries}] {type(e).__name__} → {wait:.2f}s 待機")
time.sleep(wait)
# 期待出力: 一時的な DEADLINE_EXCEEDED は自動回復し、ログにリトライ履歴が残るリトライ実装の細かい設計(どのエラーを再試行するか、最大何回までか、サーキットブレーカーをどう絡めるか)については、Gemini API のエラーハンドリングとリトライパターン で詳しく整理していますので、本番投入前に併せて読んでいただくと安心です。
それでも解決しない時に確認するもう一段深い切り分け
5つのポイントを試しても改善しない場合、次に疑うべきなのは「実は別のレイヤーで切れている」可能性です。私が遭遇した実例では、以下のようなケースがありました。
- ロードバランサ(NGINX や ALB)の
proxy_read_timeoutが 60 秒に設定されていて、そこで切れていた - Cloudflare Workers の場合は CPU タイム上限(無料プラン10ms / 有料プラン50ms)に当たっていた
- 実は API キーが別プロジェクトのもので、内部的に別ルーティングになっていた
特にエッジランタイム系で運用している場合は、Gemini API そのものではなくエッジ側の制限に当たっているケースが多いです。同じ症状でも対処が大きく変わるので、Cloudflare Workers の subrequest 上限に当たる時のトラブルシューティング と Gemini API のレスポンスが遅い・タイムアウトする時の対処法 を合わせて確認していただくと、見落としを潰しやすくなります。
次にやること
DEADLINE_EXCEEDED の頻度を本気で下げたい場合、まず最初に手を入れるべきは「ポイント1の計測」です。所要時間の分布が見えないままタイムアウトを伸ばしたりリトライを増やしたりしても、症状が見えなくなるだけで根本は変わりません。今日のうちに time.perf_counter() のラッパーを入れて、24 時間分のリクエスト所要時間を観察してみてください。そこから「クライアント側で切れているのか、サーバー側で長いのか、特定の入力だけ遅いのか」が見えるはずです。
私自身、計測を入れた瞬間に「実は上位 5% の長尺 PDF だけが原因だった」と分かり、そこから 30 分でほぼ全件解消できた経験があります。手を動かす前に、まず分布を見るところから始めていただくのがおすすめです。