April 20, 2026

[1つのモデルで429制限発生時に、Google Geminiプロバイダー全体にバックオフが適用される問題] - Google Gemini Provider: 429 Rate Limit Scopes to Entire Provider Instead of Specific Model

1つのGoogle Geminiモデルがレート制限(429)に達すると、OpenClawゲートウェイが'google'プロバイダー全体にバックオフを適用し、独立した配额を持つ他の無関係なモデルへのアクセスをブロックします。

🔍 症状

主な症状

特定のGoogle Geminiモデルのクォータが使い果たされると、そのモデルとは独立したクォータ割り当てを持つ他のモデルへのすべてのリクエストも、レート制限エラーで失敗します。

エラーの出力例

直接のAPIレスポンス(Googleからの429):

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{
  "error": {
    "code": 429,
    "message": "Resource has been exhausted (e.g. check quota).",
    "status": "RESOURCE_EXHAUSTED"
  }
}

バックオフ発動後のOpenClawゲートウェイレスポンス:

{
  "error": {
    "type": "rate_limit_exceeded",
    "provider": "google",
    "message": "Provider 'google' is currently in cooldown due to rate limiting. Retry-After: 120s",
    "retry_after": 120
  }
}

動作上の症状

  • モデル分離なしgemini-3.1-pro-preview-customtoolsからgemini-3.0-pro-previewへの切り替えでは機能が回復しません。
  • 長時間の利用不可:すべてのgoogleプロバイダへのリクエストが、プロバイダレベルのクールダウンが期限切れるまで失敗します。
  • フォールバックパスなし:レート制限イベント発生時、同じプロバイダ内の代替モデルはフォールバックとして機能できません。
  • ゲートウェイレベルでの拒否:リクエストはGoogleのAPIに到達する前に、OpenClawゲートウェイレイヤーで拒否される場合があります。

再現シナリオ

# Step 1: Request to rate-limited model
curl -X POST https://api.openclaw.io/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.1-pro-preview-customtools", "messages": [{"role": "user", "content": "test"}]}'
# Response: 429 from Google API

# Step 2: Immediate fallback to another model
curl -X POST https://api.openclaw.io/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.0-pro-preview", "messages": [{"role": "user", "content": "test"}]}'
# Expected: Request proceeds to Google API
# Actual: 429 or backoff error from OpenClaw gateway

🧠 原因

アーキテクチャ分析

根本原因は、OpenClawゲートウェイのリトライ/バックオフメカニズム内のプロバイダレベルのレート制限トラッキングの実装にあります。

障害のシーケンス

  1. gemini-3.1-pro-preview-customtoolsへのリクエスト:モデル固有のデプロイメントがGoogleのAPIから429 RESOURCE_EXHAUSTEDを受け取る。
  2. ゲートウェイが429を傍受:OpenClawのエラーハンドリングミドルウェアが429レスポンスをキャッチする。
  3. プロバイダレベルのバックオフ激活:特定のモデル/デプロイメントではなく、ゲートウェイがgoogleプロバイダ識別子にクールダウンタイマーを設定する。
  4. gemini-3.0-pro-previewへの後続のリクエスト:ゲートウェイがgoogleプロバイダがクールダウン中かどうかを確認する。クールダウン中の場合、バックオフエラーでリクエストを先制的に拒否する。
  5. 独立したクォータを持つモデルがブロックされるgemini-3.0-pro-previewは完全に別のクォータ割り当てを持っている可能性があるが、アクセスできない。

コードレベルの根本原因

レート制限トラッキングは、以下のようなデータ構造を使用している可能性があります:

// Simplified representation of current behavior
const providerBackoff = {
  "google": {
    cooldownUntil: 1699999999999,  // Unix timestamp
    reason: "rate_limit",
    retryAfter: 120
  }
};

// Backoff check
function shouldReject(provider) {
  return providerBackoff[provider]?.cooldownUntil > Date.now();
}

問題点:バックオフがプロバイダ名(“google”)而不是由模型或部署标识符キーになっている。

Google Gemini APIのクォータアーキテクチャ

Google Gemini APIは以下の形式で動作します:

  • モデル固有のクォータ:各モデル(例:gemini-3.1-pro-preview-customtools)は独立したレート制限を持つ。
  • プロジェクトレベルのクォータ:すべてのモデルに影響するより広範な制限だが、通常ははるかに高い。
  • リージョンエンドポイント:独立した制限を持つ場合がある。

異なるコードパス

シナリオ現在の動作期待される動作
モデルAが429を受けるgoogleプロバイダ全体がブロックモデルAのみブロック
モデルAのクォータが使い果たされるモデルBが利用不可モデルBはクォータが利用可能なら継続
プロバイダバックオフがアクティブゲートウェイがレイヤー7で拒否リクエストがAPIに送信される

🛠️ 解決手順

オプション1:モデルスコープのレート制限を有効にする(推奨)

OpenClawがモデルごとのレート制限トラッキングをサポートしている場合、ゲートウェイをモデルレベルのバックオフを使用するように設定します:

変更前(openclaw.yaml):

providers:
  google:
    api_key: "${GOOGLE_API_KEY}"
    rate_limit:
      strategy: "provider"  # Current: blocks entire provider
      retry_after: 120

変更後:

providers:
  google:
    api_key: "${GOOGLE_API_KEY}"
    rate_limit:
      strategy: "model"  # Changed: per-model tracking
      retry_after: 120
      scope: "deployment"  # Granularity: model/deployment level

オプション2:モデル固有のフォールバックを設定

レート制限されたモデルをバイパスするための明示的なフォールバックチェーンを定義します:

変更前:

models:
  - name: "gemini-3.1-pro-preview-customtools"
    provider: "google"

変更後:

models:
  - name: "gemini-3.1-pro-preview-customtools"
    provider: "google"
    fallback_models:
      - "gemini-3.0-pro-preview"
      - "gemini-pro"

  - name: "gemini-3.0-pro-preview"
    provider: "google"
    fallback_models:
      - "gemini-pro"

オプション3:プロバイダのクールダウン粒度を増加(コード修正)

OpenClawのソースコードへのアクセスがある場合、レート制限トラッキングを修正します:

ステップ1:レート制限ハンドラを特定

429レスポンスを処理するファイルを見つけます。通常は以下の場所にあります:

src/gateway/middleware/rate-limit-handler.ts
src/providers/google/error-handler.ts

ステップ2:バックオフキーをプロバイダからモデルに変更

// BEFORE (provider-level)
providerBackoff[provider] = {
  cooldownUntil: Date.now() + retryAfter * 1000,
  reason: "rate_limit"
};

// AFTER (model-level)
const modelKey = `${provider}:${model}`;
modelBackoff[modelKey] = {
  cooldownUntil: Date.now() + retryAfter * 1000,
  reason: "rate_limit",
  model: model,
  provider: provider
};

ステップ3:拒否チェックを更新

// BEFORE
function shouldReject(request) {
  const provider = request.provider;
  return providerBackoff[provider]?.cooldownUntil > Date.now();
}

// AFTER
function shouldReject(request) {
  const modelKey = `${request.provider}:${request.model}`;
  const providerKey = request.provider;
  
  // Check model-specific backoff first
  if (modelBackoff[modelKey]?.cooldownUntil > Date.now()) {
    return { rejected: true, reason: "model_rate_limited" };
  }
  
  // Fallback to provider-level for shared limits only
  if (providerBackoff[providerKey]?.cooldownUntil > Date.now()) {
    return { rejected: true, reason: "provider_rate_limited" };
  }
  
  return { rejected: false };
}

オプション4:複数プロバイダインスタンスによる回避策

独立したクォータを持つモデルのために別々のプロバイダ設定を作成します:

providers:
  google-gemini-31:
    api_key: "${GOOGLE_API_KEY}"
    models:
      - "gemini-3.1-pro-preview-customtools"
    rate_limit:
      retry_after: 60

  google-gemini-30:
    api_key: "${GOOGLE_API_KEY}"
    models:
      - "gemini-3.0-pro-preview"
    rate_limit:
      retry_after: 60

  google-gemini-pro:
    api_key: "${GOOGLE_API_KEY}"
    models:
      - "gemini-pro"
    rate_limit:
      retry_after: 60

🧪 検証

テスト1:修正後のモデルレベル分離の確認

# Step 1: Trigger rate limit on model A
curl -X POST https://api.openclaw.io/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.1-pro-preview-customtools", "messages": [{"role": "user", "content": "test"}]}'

# Expected: 429 from Google API
# Verify with: echo $? (should be non-zero)

# Step 2: Immediately test model B access
curl -X POST https://api.openclaw.io/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.0-pro-preview", "messages": [{"role": "user", "content": "test"}]}'

# Expected: 200 OK or valid API response (not gateway backoff error)

テスト2:モデル固有のバックオフ状態の確認

ゲートウェイの内部状態を確認します(管理者エンドポイントで公開されている場合):

GET /admin/rate-limit-status

# Expected response structure:
{
  "providers": {
    "google": {
      "cooldown": false,
      "models": {
        "gemini-3.1-pro-preview-customtools": {
          "cooldown": true,
          "retry_after": 120,
          "expires_at": "2024-01-15T10:30:00Z"
        },
        "gemini-3.0-pro-preview": {
          "cooldown": false
        }
      }
    }
  }
}

テスト3:同時モデル可用性テスト

# Run concurrent requests to different models
for model in "gemini-3.1-pro-preview-customtools" "gemini-3.0-pro-preview" "gemini-pro"; do
  echo "Testing: $model"
  curl -s -o /dev/null -w "%{http_code}\n" \
    -X POST https://api.openclaw.io/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d "{\"model\": \"$model\", \"messages\": [{\"role\": \"user\", \"content\": \"test\"}]}"
done

# Expected: 
# gemini-3.1-pro-preview-customtools: 429 (rate limited)
# gemini-3.0-pro-preview: 200 (independent quota)
# gemini-pro: 200 (independent quota)

テスト4:バックオフ期限切れの確認

# Wait for cooldown to expire
echo "Waiting for model cooldown expiration..."
sleep 130  # retry_after + buffer

# Verify previously rate-limited model is accessible
curl -X POST https://api.openclaw.io/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.1-pro-preview-customtools", "messages": [{"role": "user", "content": "test"}]}'

# Expected: 200 OK

成功基準

  • gemini-3.1-pro-preview-customtoolsでレート制限後でも、他のgoogleモデルはアクセス可能なまま。
  • ✅ モデル固有のバックオフ状態が正しくトラッキングされ、独立して期限切れになる。
  • ✅ ゲートウェイがレート制限されていないモデルへのリクエストを先制的に拒否しない。
  • ✅ プライマリモデルが利用不可の場合、フォールバックチェーンが正しく機能する。

⚠️ よくある落とし穴

環境固有のトラップ

Dockerコンテナのキャッシュ

# Pitfall: Container filesystem may cache rate limit state
# Restarting containers may not reset state if persistence is enabled

docker-compose down
docker volume prune openclaw-cache  # Clear cached state
docker-compose up -d

Kubernetesボリュームマウント

レート制限トラッキングに永続ボリュームを使用している場合:

# Verify PVC is not stale after config changes
kubectl get pvc | grep openclaw
kubectl describe pvc openclaw-cache

# May need to delete and recreate if schema changed
kubectl delete pvc openclaw-cache
# Then restart deployments

macOS開発環境

# Pitfall: Local rate limit state may persist across terminal sessions
# Clear any local state files
rm -rf ~/.openclaw/cache/*
rm -rf .openclaw/state.json

設定ミス

フォールバックチェーンの不適切なプロバイダ名

# WRONG: Typos in provider name cause silent failures
models:
  - name: "gemini-3.0-pro-preview"
    provider: "googel"  # Typo - will not match actual provider

# CORRECT:
models:
  - name: "gemini-3.0-pro-preview"
    provider: "google"

重複するモデル宣言

# WRONG: Same model declared multiple times
models:
  - name: "gemini-3.0-pro-preview"
    provider: "google"
  - name: "gemini-3.0-pro-preview"  # Duplicate
    provider: "google"
    fallback_models: [...]

APIキースコープの不一致

# Pitfall: Google API keys may have different quotas per project
# If using separate provider instances, ensure they use keys with adequate quotas

# Verify in Google Cloud Console:
# APIs & Services > Enabled APIs > Vertex AI API > Quotas

エッジケースのテスト

最後に利用可能なモデルでのレート制限

# Scenario: All models under a provider are rate-limited
# Expected: Should return clear error, not silent success

# Verify error response includes all affected models
curl -X POST https://api.openclaw.io/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.0-pro-preview", "messages": [{"role": "user", "content": "test"}]}'

# Check response contains actionable information
# Should NOT be empty 200 OK

素早いモデル切り替え

# Pitfall: Race condition during rapid switching may bypass backoff
# Test with concurrent requests

ab -n 100 -c 10 -T 'application/json' \
  -p request.json \
  https://api.openclaw.io/v1/chat/completions

# Verify all requests are properly rate-limited or processed

🔗 関連するエラー

エラーコード説明関連性
429 RESOURCE_EXHAUSTEDGoogle APIがレート制限エラーを返したプロバイダバックオフを引き起こす元エラーの原因
503 Service Unavailableプロバイダが一時的に利用不可長時間続くプロバイダバックオフの下流エラー
500 Internal Server Errorバックオフ処理中のゲートウェイエラーレート制限ミドルウェアでの未処理例外
ENOTFOUNDGoogle APIのDNS解決失敗無関係だがレート制限と誤診される可能性あり
ETIMEDOUTGoogle APIへの接続タイムアウト無関係だがincorrect backoff logic をトリガーする可能性がある
INVALID_ARGUMENTGemini APIへの不正なリクエストエラーハンドリングでレート制限として誤って処理される可能性あり

歴史的背景

この問題は、マルチテナントAPIゲートウェイ設計におけるより広範なパターンに関連しています:

  • 過度に広範なサーキットブレーカー:モデル/デプロイメントレベルで動作すべきサーキットブレーカーパターンをプロバイダレベルで適用。
  • 共有状態の衝突:複数の独立したリソースが単一のレート制限カウンターを共有。
  • 不十分なエラーコンテキスト:Googleからの429レスポンスには、どのクォータが使い果たされたかを指定するretryInfoが含まれているが、解析されていない可能性がある。

関連するGitHubイシュー

エビデンスとソース

このトラブルシューティングガイドは、FixClaw Intelligence パイプラインによってコミュニティの議論から自動的に合成されました。