個人開発で Dolice Labs の複数サイトを Google Cloud 上で回していると、ローカルでは何ごともなく動いていた Gemini API が、本番に出した途端に 403 や 429 を返して止まる——という場面に何度も出くわしてきました。私自身、IAM のロール一つ、サービスアカウントの鍵一つで半日を溶かした経験があり、そのたびに「エラー文字列から原因の層を一発で切り分ける手順」を手元に積み上げてきました。ここからは、本番特有のエラーを認証・ネットワーク・クォータ・モデル非推奨の層に分けて、診断ステップと動くコードで一つずつ潰していきます。
Gemini API の2つのエントリーポイントと本番での選択基準
Gemini API には大きく2つのアクセス経路があります。この違いを理解していないと、本番環境での設定ミスに直結します。
Google AI Studio API(generativelanguage.googleapis.com)
- APIキーで認証する、シンプルな REST API
- 迅速なプロトタイピングや個人プロジェクト向け
- VPC Service Controls や組織ポリシーの適用対象外
- エンタープライズレベルの SLA なし
Vertex AI Gemini API(aiplatform.googleapis.com)
- サービスアカウント / OAuth2 で認証する
- エンタープライズ向け:SLA・監査ログ・VPC対応・CMEK 対応
- IAM による細かなアクセス制御が可能
- リージョン指定でデータ所在地のコンプライアンス対応が可能
本番環境には Vertex AI を推奨します。 理由は、組織のセキュリティポリシー・コンプライアンス要件に対応できること、IAM で最小権限の原則を実装できること、VPC 内でのプライベートネットワーク接続が利用できること、そして Cloud Monitoring / Cloud Logging との統合ができることです。
IAM・サービスアカウントエラーの診断と修正
よくあるエラー:PERMISSION_DENIED
google.api_core.exceptions.PermissionDenied: 403 Permission 'aiplatform.endpoints.predict' denied
このエラーは IAM ロールが不足していることを意味します。
必要な IAM ロールの確認:
# 現在のサービスアカウントに割り当てられたロールを確認
gcloud projects get-iam-policy YOUR_PROJECT_ID \
--flatten="bindings[].members" \
--filter="bindings.members:serviceAccount:YOUR_SA@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
--format="table(bindings.role)"
Vertex AI Gemini API に必要な最小限のロール:
roles/aiplatform.user:Vertex AI エンドポイントへの推論リクエスト送信
roles/ml.viewer:モデルの一覧・詳細確認(任意)
ロールの付与:
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:YOUR_SA@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
よくあるエラー:サービスアカウントキーが見つからない
本番環境では、サービスアカウントキー JSON ファイルをアプリケーションに同梱するのはセキュリティ上推奨されません。代わりに Workload Identity Federation や Application Default Credentials(ADC)を活用しましょう。
ADC を使った認証(推奨):
import vertexai
from vertexai.generative_models import GenerativeModel
# Cloud Run / GKE / Compute Engine 上では、
# サービスアカウントが自動的に使用される
# GOOGLE_APPLICATION_CREDENTIALS 環境変数の設定は不要
vertexai.init(project="YOUR_PROJECT_ID", location="us-central1")
model = GenerativeModel("gemini-2.0-flash-001")
response = model.generate_content("日本語で挨拶してください")
print(response.text)
ローカル開発では gcloud の認証情報を使用:
# ローカルマシンで ADC を設定
gcloud auth application-default login
# 特定のサービスアカウントを偽装してテストする場合
gcloud auth application-default login \
--impersonate-service-account=YOUR_SA@YOUR_PROJECT_ID.iam.gserviceaccount.com
サービスアカウントの偽装エラー
google.auth.exceptions.TransportError: Unable to fetch token
このエラーはしばしば、サービスアカウントの偽装(impersonation)の権限不足が原因です。
偽装元のアカウントに roles/iam.serviceAccountTokenCreator ロールが必要です。
gcloud iam service-accounts add-iam-policy-binding \
TARGET_SA@YOUR_PROJECT_ID.iam.gserviceaccount.com \
--member="user:YOUR_EMAIL@example.com" \
--role="roles/iam.serviceAccountTokenCreator"
API 有効化エラーの確認と対処
Gemini API を Vertex AI 経由で使う場合、プロジェクトで複数の API を有効化する必要があります。
必要な API の一括確認と有効化:
# 必要なAPI一覧を確認
gcloud services list --enabled --filter="name:(aiplatform OR ml OR storage)"
# 不足しているAPIを有効化
gcloud services enable \
aiplatform.googleapis.com \
ml.googleapis.com \
storage.googleapis.com \
--project=YOUR_PROJECT_ID
API 有効化後に反映されるまでの待機:
API の有効化には数分かかることがあります。有効化直後にリクエストすると SERVICE_DISABLED エラーが返ることがあります。以下のコードで確認できます。
from googleapiclient.discovery import build
from google.auth import default
credentials, project = default()
service = build('serviceusage', 'v1', credentials=credentials)
response = service.services().get(
name=f"projects/{project}/services/aiplatform.googleapis.com"
).execute()
print(f"API状態: {response.get('state')}") # "ENABLED" であることを確認
クォータとレート制限エラーの診断
429 Resource Exhausted エラー
google.api_core.exceptions.ResourceExhausted: 429 Quota exceeded for quota metric
'generate_requests_per_minute_per_project_per_base_model'
クォータの確認方法:
# プロジェクトのクォータを確認
gcloud compute project-info describe --project=YOUR_PROJECT_ID
# または Cloud Console で確認
# IAM & Admin → Quotas → フィルタ: "aiplatform"
クォータ別の対処法:
分あたりのリクエスト数(RPM)を超過している場合は、エクスポネンシャルバックオフでリトライします。
import time
import random
import google.api_core.exceptions
import vertexai
from vertexai.generative_models import GenerativeModel
vertexai.init(project="YOUR_PROJECT_ID", location="us-central1")
model = GenerativeModel("gemini-2.0-flash-001")
def generate_with_retry(prompt: str, max_retries: int = 5) -> str:
"""クォータエラーへの対応込みリトライ実装"""
for attempt in range(max_retries):
try:
response = model.generate_content(prompt)
return response.text
except google.api_core.exceptions.ResourceExhausted as e:
if attempt >= max_retries - 1:
raise
# Retry-Info ヘッダーを確認(ある場合は優先)
retry_delay = None
if hasattr(e, 'metadata'):
for key, value in e.metadata:
if key == 'retry-info':
# メタデータからリトライ遅延を取得
retry_delay = float(value)
break
if retry_delay is None:
# エクスポネンシャルバックオフ + ジッター
retry_delay = min(2 ** attempt + random.uniform(0, 1), 60)
print(f"クォータ超過: {retry_delay:.1f}秒後にリトライ (試行 {attempt + 1}/{max_retries})")
time.sleep(retry_delay)
except google.api_core.exceptions.DeadlineExceeded:
if attempt >= max_retries - 1:
raise
time.sleep(2 ** attempt)
return ""
クォータの上限引き上げ申請:
RPM やトークン数の上限に継続的に到達する場合は、Google Cloud Console → IAM & Admin → Quotas から上限引き上げをリクエストできます。承認には数日かかることがあります。
1分あたりのトークン数(TPM)超過
大量のテキストを処理する場合、RPM より TPM(Tokens Per Minute)の上限に先に到達することがあります。
対処法:バッチサイズの調整とトークン数の事前計算
from vertexai.generative_models import GenerativeModel
model = GenerativeModel("gemini-2.0-flash-001")
def estimate_tokens(text: str) -> int:
"""大まかなトークン数を事前計算(1トークン≒4文字の目安)"""
return len(text) // 4
def batch_process_with_tpm_control(
texts: list[str],
tpm_limit: int = 100000
) -> list[str]:
"""TPM制限を考慮したバッチ処理"""
results = []
current_minute_tokens = 0
minute_start = time.time()
for text in texts:
estimated_tokens = estimate_tokens(text)
# 1分あたりのトークン数をチェック
if current_minute_tokens + estimated_tokens > tpm_limit * 0.9: # 90%で調整
elapsed = time.time() - minute_start
if elapsed < 60:
wait_time = 60 - elapsed + 1 # 次の分まで待機
print(f"TPM制限に近い: {wait_time:.1f}秒待機")
time.sleep(wait_time)
current_minute_tokens = 0
minute_start = time.time()
response = generate_with_retry(text)
results.append(response)
current_minute_tokens += estimated_tokens
return results
VPC Service Controls と Private Service Connect
エンタープライズ環境では、VPC Service Controls(VPC-SC)を使用して Gemini API へのアクセスを制限することがあります。VPC-SC の設定ミスは、正しい IAM 権限を持っていてもアクセスを遮断します。
VPC-SC エラーの特定
google.api_core.exceptions.PermissionDenied: 403 Request is prohibited by organization's policy.
vpcServiceControlsUniqueIdentifier: XXXXXXXX
このエラーは IAM の問題ではなく、VPC-SC ポリシーによるブロックです。
診断コマンド:
# VPC-SC アクセスレポートでブロックされたリクエストを確認
gcloud access-context-manager perimeters list \
--policy=YOUR_POLICY_NUMBER
# Cloud Audit Logs で詳細を確認
gcloud logging read \
'protoPayload.@type="type.googleapis.com/google.cloud.audit.AuditLog"
AND protoPayload.status.code=7
AND resource.type="audited_resource"' \
--freshness=1h \
--format="json" | head -100
対処法:VPC-SC ポリシーへのサービスの追加
組織の管理者に依頼して、aiplatform.googleapis.com を VPC-SC ペリメータのアクセス可能なサービスとして追加してもらう必要があります。
# 管理者が実行: VPC-SC ペリメータにサービスを追加
gcloud access-context-manager perimeters update PERIMETER_NAME \
--add-restricted-services=aiplatform.googleapis.com \
--policy=POLICY_NUMBER
Private Service Connect の設定
本番環境でインターネットを経由せずに Gemini API を使用したい場合は、Private Service Connect(PSC)を利用します。
import vertexai
# PSCエンドポイントを使用した初期化
vertexai.init(
project="YOUR_PROJECT_ID",
location="us-central1",
api_endpoint="YOUR_PSC_ENDPOINT.p.googleapis.com"
)
PSC エンドポイントは Cloud Console → Vertex AI → プライベートサービス接続から確認できます。
マルチリージョン設定とフェイルオーバー
本番環境では単一リージョンに依存するのは避けたいところです。Gemini API は複数のリージョンに対応しています。
import vertexai
from vertexai.generative_models import GenerativeModel
import google.api_core.exceptions
import logging
logger = logging.getLogger(__name__)
REGION_PRIORITY = ["us-central1", "us-east4", "europe-west4", "asia-northeast1"]
def generate_with_region_failover(
prompt: str,
project_id: str
) -> tuple[str, str]:
"""マルチリージョンフェイルオーバー付き生成"""
for region in REGION_PRIORITY:
try:
vertexai.init(project=project_id, location=region)
model = GenerativeModel("gemini-2.0-flash-001")
response = model.generate_content(prompt)
return response.text, region
except google.api_core.exceptions.ServiceUnavailable as e:
logger.warning(f"リージョン {region} が利用不可: {e}")
continue
except google.api_core.exceptions.DeadlineExceeded as e:
logger.warning(f"リージョン {region} タイムアウト: {e}")
continue
raise RuntimeError("すべてのリージョンで生成に失敗しました")
# 利用例
result, used_region = generate_with_region_failover(
"本番環境のフェイルオーバーテスト",
"YOUR_PROJECT_ID"
)
print(f"リージョン {used_region} で生成成功")
Cloud Monitoring による可観測性の設定
本番運用には適切な監視が欠かせません。Vertex AI は Cloud Monitoring と統合されており、以下のメトリクスが利用可能です。
from google.cloud import monitoring_v3
import time
def create_gemini_api_alerts(project_id: str):
"""Gemini API の重要アラートを設定"""
client = monitoring_v3.AlertPolicyServiceClient()
project_name = f"projects/{project_id}"
# クォータ使用率 80% 超過アラート
quota_alert = monitoring_v3.AlertPolicy(
display_name="Gemini API Quota Usage > 80%",
conditions=[
monitoring_v3.AlertPolicy.Condition(
display_name="High quota usage",
condition_threshold=monitoring_v3.AlertPolicy.Condition.MetricThreshold(
filter='resource.type="aiplatform.googleapis.com/Location"'
' AND metric.type="aiplatform.googleapis.com/quota/online_prediction_requests_per_base_model/usage"',
comparison=monitoring_v3.ComparisonType.COMPARISON_GT,
threshold_value=0.8,
duration={"seconds": 300},
)
)
],
notification_channels=["projects/YOUR_PROJECT_ID/notificationChannels/YOUR_CHANNEL_ID"],
combiner=monitoring_v3.AlertPolicy.ConditionCombinerType.OR
)
policy = client.create_alert_policy(
name=project_name,
alert_policy=quota_alert
)
print(f"アラートポリシー作成: {policy.name}")
確認すべき主要メトリクス:
リクエスト数とエラー率は aiplatform.googleapis.com/prediction/online/request_count で確認できます。レイテンシ分布は aiplatform.googleapis.com/prediction/online/response_latencies を監視します。クォータ使用率が 80% を超えたらアラートを設定しましょう。
組織ポリシーによるアクセス制限エラー
Google Cloud 組織では、組織ポリシーによって特定のリージョンや機能が制限されていることがあります。
google.api_core.exceptions.PermissionDenied: 403
RESOURCE_LOCATION_POLICY_VIOLATION
このエラーは、使用しようとしているリージョンが組織ポリシーで許可されていないことを意味します。
確認方法:
# 組織ポリシーのリソースロケーション制約を確認
gcloud resource-manager org-policies describe \
constraints/gcp.resourceLocations \
--project=YOUR_PROJECT_ID
対処法は、管理者に依頼して対象リージョンを組織ポリシーの許可リストに追加してもらうか、ポリシーで許可されているリージョンに変更することです。
モデルの非推奨・GA 切り替えで噴き出すエラーを先回りで捕まえる
本番でいちばん厄介なのは、自分のコードを一行も変えていないのに、ある日突然エラーが出る種類の障害です。その代表が、参照しているモデルの非推奨化と、GA への静かな切り替えです。たとえば 2026/06/25 には gemini-3.1-flash-image-preview と gemini-3-pro-image-preview が停止予定で、これらを直接名前で叩いているパイプラインは当日 NOT_FOUND で全滅します。3.5 Flash が GA になったタイミングでも、エイリアス越しに既定モデルが差し替わって、出力の粒度やコストが静かに変わることがあります。
よくあるのは、モデル名をハードコードしたうえで例外を握りつぶしてしまう書き方です。
import google.generativeai as genai
genai.configure(api_key="YOUR_API_KEY")
# モデル名をハードコード。停止・改名に弱い
model = genai.GenerativeModel("gemini-3.1-flash-image-preview")
def generate(prompt: str):
try:
return model.generate_content(prompt).text
except Exception:
# 原因の層が分からないまま握りつぶしてしまう
return None
これだと、なぜ落ちたのかがログから読み取れません。本番では、起動時にモデルの実在を確認し、非推奨・停止を検知したら明示的に後継へフォールバックする preflight を入れておくと、停止日をまたいでも沈黙の全滅を避けられます。
import logging
import google.generativeai as genai
from google.api_core.exceptions import NotFound
genai.configure(api_key="YOUR_API_KEY")
log = logging.getLogger("gemini.preflight")
# 非推奨モデル -> 後継モデルのフォールバック表(停止日付き)
DEPRECATIONS = {
"gemini-3.1-flash-image-preview": ("gemini-3.5-flash", "2026-06-25"),
"gemini-3-pro-image-preview": ("gemini-3.5-flash", "2026-06-25"),
}
def resolve_model(requested: str) -> str:
"""起動時に1回だけ実行。停止・改名を検知して後継へ寄せる。"""
target = requested
if requested in DEPRECATIONS:
successor, shutdown = DEPRECATIONS[requested]
log.warning("model %s は %s に停止予定。%s へフォールバックします",
requested, shutdown, successor)
target = successor
# 実在確認:存在しなければ NotFound が飛ぶ
try:
genai.get_model(f"models/{target}")
except NotFound:
log.error("後継モデル %s も見つかりません。設定を確認してください", target)
raise
return target
MODEL_NAME = resolve_model("gemini-3.1-flash-image-preview")
model = genai.GenerativeModel(MODEL_NAME)
def generate(prompt: str) -> str | None:
try:
resp = model.generate_content(prompt)
except NotFound:
log.error("実行時にモデルが消失しました。preflight をやり直してください")
raise
# 要求と応答のモデルがずれていないか毎回突き合わせる
used = getattr(resp, "model_version", MODEL_NAME)
if used != MODEL_NAME:
log.info("応答モデルが %s に切り替わりました(要求: %s)", used, MODEL_NAME)
return resp.text
押さえておきたいのは3点です。第一に、起動時の preflight で NOT_FOUND を握り、フォールバック表で後継モデルへ寄せること。第二に、応答に含まれる実際のモデル識別子をログに残し、要求と応答がずれていないかを毎回突き合わせること。第三に、停止日が判明している非推奨モデルは設定ファイルに期限付きで持ち、期限が来る前に CI で警告を出すことです。私自身、プレビュー機能に寄りかかったパイプラインで停止日に足をすくわれた経験があり、この preflight を入れてからは、非推奨の予告を見落としても本番が黙って全滅することはなくなりました。
全体を振り返って:本番環境 Gemini API エラー診断フロー
本番環境で Gemini API エラーが発生した際の診断フローをまとめます。
Step 1: エラーの種類を確認する。PERMISSION_DENIED → IAM ロール・サービスアカウント・VPC-SC を確認。RESOURCE_EXHAUSTED → クォータ使用量を確認してリトライ実装。SERVICE_UNAVAILABLE → API 有効化状態・リージョン障害を確認。DEADLINE_EXCEEDED → タイムアウト設定・マルチリージョンフェイルオーバーを実装。
Step 2: Cloud Audit Logs を確認する。エラーの詳細な原因は Audit Logs に記録されています。特に VPC-SC エラーは vpcServiceControlsUniqueIdentifier から詳細を特定できます。
Step 3: 最小権限の原則に立ち返る。「とりあえず Owner ロールを付与」は危険です。roles/aiplatform.user から始めて、必要に応じて追加しましょう。
本番環境の Gemini API 運用は、一度設定が整えば非常に安定して動作します。セキュアで信頼性の高い AI 機能を実装されるお手伝いができれば、とても嬉しく思います。