◉GEMINI LABEN
●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…
記事一覧/API / SDK
◈ API / SDK/2026-06-02中級

Firebase AI Logic で iOS から Gemini を呼ぶと 403 になる原因と対処

Firebase AI Logic(旧 Vertex AI in Firebase)で iOS アプリから Gemini を呼び出すと 403 PERMISSION_DENIED が返る問題。App Check の強制適用・API 未有効化・Blaze 未設定の3つの原因を切り分けて解決する手順を、実機検証の経験から整理します。

gemini-api288firebase6ios13troubleshooting59app-check

2014年からアプリ事業を続けている個人開発者として、Firebase AI Logic を自分の iOS アプリに組み込んだとき、シミュレータでは動くのに TestFlight に上げた瞬間すべての Gemini 呼び出しが 403 で落ちる、という現象に丸一日悩まされました。

エラーメッセージ自体は短く、The request is missing a valid API key や PERMISSION_DENIED としか出ないため、原因がコードなのか設定なのか判別しづらいのが厄介です。以下では、累計5,000万ダウンロードの自社アプリに Gemini を組み込む過程で私が実際にぶつかった3つの原因と、その切り分け手順を、実機検証の経験からまとめます。

まず出ている 403 の中身を確認する

Firebase AI Logic(Swift では FirebaseAI モジュール)で Gemini を呼ぶと、失敗時には次のような形でエラーが返ります。

import FirebaseAI
 
let model = FirebaseAI.firebaseAI(backend: .googleAI())
    .generativeModel(modelName: "gemini-2.5-flash")
 
do {
    let response = try await model.generateContent("こんにちは")
    print(response.text ?? "")
} catch {
    // ここで握りつぶさず、必ず error をそのまま出力する
    print("Gemini call failed:", error)
}

catch 節で error をそのまま print すると、ログに httpStatusCode = 403 と一緒に status: PERMISSION_DENIED や App Check token is invalid といった詳細が出ます。まずこの全文を読むことが切り分けの起点です。response.text が nil だから、と曖昧に判断して進めると遠回りになります。

403 の裏には、大きく分けて次の3つの原因があります。上から順に確認していくのが早道です。

原因1: App Check が強制適用されているのにトークンが出ていない(最頻出)

私のケースで犯人だったのがこれです。Firebase コンソールで App Check を「強制(Enforce)」に切り替えると、有効な App Check トークンを持たないリクエストはすべて 403 で拒否されます。シミュレータで動いて実機やTestFlightで落ちるのは、シミュレータでは debug provider のトークンが通っていたのに、実機では App Attest プロバイダを初期化していなかったためでした。

対処は、FirebaseApp.configure() の 前 に App Check プロバイダを設定することです。順序を間違えると、Firebase が初期化された後にプロバイダが差し替わり、最初の数リクエストがトークンなしで飛びます。

import FirebaseCore
import FirebaseAppCheck
 
class AppCheckSetup: NSObject, AppCheckProviderFactory {
    func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
        // iOS 14 以降の実機は App Attest、それ以前は DeviceCheck
        if #available(iOS 14.0, *) {
            return AppAttestProvider(app: app)
        }
        return DeviceCheckProvider(app: app)
    }
}
 
// AppDelegate / App 起動時、configure より前に呼ぶ
AppCheck.setAppCheckProviderFactory(AppCheckSetup())
FirebaseApp.configure()

開発中にシミュレータで検証したいときは、debug provider を使い、コンソールに出力されるデバッグトークンを Firebase コンソールの「App Check → アプリ → デバッグトークンを管理」に登録します。

#if DEBUG
AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory())
#else
AppCheck.setAppCheckProviderFactory(AppCheckSetup())
#endif
FirebaseApp.configure()

ここで一つ注意点があります。App Attest はサーバー側で実機の正当性を検証するため、TestFlight ビルドであっても初回の検証に数秒かかることがあります。アプリ起動直後に即座に Gemini を呼ぶ設計だと、トークンがまだ発行されておらず 403 になることがあるので、最初の呼び出しはユーザー操作を待つか、軽いリトライを挟むと安定します。

原因2: 必要な Google API が有効化されていない

Firebase コンソールの「AI Logic」から初回セットアップをウィザード経由で行うと、必要な API は自動で有効化されます。しかし、別の Firebase プロジェクトを流用したり、GoogleService-Info.plist だけ差し替えて使い回したりすると、API が無効のままで 403 になります。

使用するバックエンドによって必要な API が異なります。

  • Gemini Developer API バックエンド(.googleAI()): firebasevertexai.googleapis.com と Generative Language API
  • Vertex AI バックエンド(.vertexAI()): aiplatform.googleapis.com

Google Cloud Console の「API とサービス → 有効な API」で、対象プロジェクトに上記が並んでいるか確認します。コマンドで確認・有効化する場合は次の通りです。

# 有効化済みか確認
gcloud services list --enabled --project=YOUR_PROJECT_ID | grep -E "aiplatform|firebasevertexai"
 
# 足りなければ有効化(Vertex AI バックエンドの例)
gcloud services enable aiplatform.googleapis.com --project=YOUR_PROJECT_ID

私の経験上、既存アプリに後から AI 機能を足すときにこの罠を踏みやすいです。新規ウィザードを通っていないプロジェクトは、まずここを疑ってください。

原因3: Blaze プラン(従量課金)が未設定

Vertex AI バックエンドを使う場合、プロジェクトが無料の Spark プランのままだと、課金が必要なため 403 もしくは課金関連のエラーになります。Firebase コンソールの「使用量と請求」から Blaze プランへアップグレードすると解消します。

Gemini Developer API バックエンドは無料枠の範囲なら Spark プランでも動きますが、本番アプリでレート制限を避けたいなら、いずれにせよ Blaze への移行を検討することになります。コストが不安な場合は、Google Cloud の予算アラートを設定しておくと安心です。

切り分けの順序をまとめると

403 に当たったら、次の順で確認すると最短で原因にたどり着けます。

  1. catch でエラー全文を出力し、App Check token is invalid の文字列があるか確認する → あれば原因1
  2. なければ Google Cloud Console で対象 API が有効か確認する → 無効なら原因2
  3. API も有効なら、課金プランが Blaze かを確認する → Spark なら原因3
  4. すべて満たしているのに 403 が続く場合は、GoogleService-Info.plist が呼び出し元プロジェクトのものと一致しているかを確認する(別環境の plist を混入させていると、コンソールで見ている設定と実際の宛先がずれます)

特に1と4は、コンソール上の設定だけ見ていても気づけません。実機ログのエラー全文と、ビルドに含まれている plist の PROJECT_ID を突き合わせるのが確実です。

再発を防ぐために

私は今、AI 機能を持つアプリのリリースチェックリストに「App Check を強制適用する前に、実機(TestFlight)で App Attest トークンが発行されることを確認する」という項目を入れています。強制適用は最後に切り替える設定であって、開発初期から有効にすると、原因の切り分けが一気に難しくなるからです。

開発中は App Check を「強制」ではなく「未強制(モニタリングのみ)」にしておき、トークンの発行状況をコンソールで観察してから本番で強制に切り替える。この順番にしてから、403 に振り回されることがなくなりました。

同じところでつまずいている方の参考になれば幸いです。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

◈ API / SDK2026-10-16
models は 200 なのに generateContent だけ 403 になる日 — 切り分けの順番を先に決めておく
モデル一覧の取得は 200 で通るのに、生成リクエストだけが 403 permission_denied で止まるときの切り分け手順です。キー・プロジェクト・請求の順に確かめる小さな診断スクリプトと、リトライしてはいけない理由、サポートへ渡す材料の揃え方をまとめました。
◈ API / SDK2026-06-23
Gemini 3.2 Pro の Function Calling を iOS/Android アプリに統合する実装パターン
Gemini 3.2 Pro の Function Calling を iOS/Android アプリに統合する実装ガイド。SwiftUI・Kotlin 両対応の動くコード例と、個人開発の壁紙アプリで実証した本番設計パターン(コスト・レイテンシ・段階ロールアウト・回帰テスト)を解説します。
◈ API / SDK2026-05-31
Gemini 2.5 Flash で App Store のキーワード欄を多言語最適化した1ヶ月の所感
App Store Connect の100文字キーワード欄を、40本の壁紙アプリ×複数ロケールで Gemini 2.5 Flash に下書きさせた1ヶ月の運用ログです。CJK の文字数、タイトル重複の排除、禁止語の落とし穴、ロケールごとの効きどころの違いまで、現場でつまずいた点を淡々と記録します。