GEMINI LABEN
NANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定ですNANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定です
記事一覧/API / SDK
API / SDK/2026-05-12中級

Gemini File APIのファイルが「PROCESSING」のまま止まる:タイムアウト処理とリトライ設計

Gemini File APIでアップロードしたファイルがPROCESSING状態のまま進まない問題を解説。ポーリングの正しい実装・タイムアウト設計・失敗時のクリーンアップまで、実際のコードで解決します。

gemini-api279file-api6troubleshooting57python103error-handling8

個人で壁紙アプリを開発していたころ、画像の自動分類機能にGemini File APIを組み込んだことがあります。アップロード後に file.state を確認すると PROCESSING と表示されるのは正常な流れのはずでした。ところが何分待っても状態が変わらず、次のリクエストに進めないまま処理が止まってしまう。

はじめはネットワークの問題かと思いましたが、原因はポーリングのロジックにありました。同じ問題に何度か直面するうちに、対処のパターンが整理されてきたので記録しておきます。

File APIの PROCESSING 状態とは何か

Gemini File APIにファイルをアップロードすると、即座に使えるわけではありません。特に動画ファイルや大きなPDFは、Googleのサーバーが内部でトランスコードや解析処理を行い、その間 PROCESSING 状態が続きます。

処理が完了すると ACTIVE に変わり、はじめてモデルへのリクエストに使えるようになります。FAILED になった場合は再アップロードが必要です。

import google.generativeai as genai
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
 
# ファイルをアップロード
uploaded_file = genai.upload_file("sample_video.mp4", mime_type="video/mp4")
print(uploaded_file.state)  # → FileState.PROCESSING

状態の遷移は PROCESSING → ACTIVE または PROCESSING → FAILED の2択です。ACTIVE になるまで待たずにモデルに渡すと、次のエラーが発生します。

google.api_core.exceptions.FailedPrecondition: 400 File is not yet ready

止まる原因は3パターンある

PROCESSING のまま進まない問題には、実際には3つの独立した原因があります。症状が似ているので混同しがちです。

パターン1:ポーリングが一度しか実行されていない

最もよくあるミスです。file.state を一度確認して PROCESSING だったら、そのまま処理を止めてしまうコードです。

# ❌ 悪い例:一度確認して諦める
file = genai.get_file(uploaded_file.name)
if file.state == genai.protos.File.State.PROCESSING:
    print("まだ処理中")
    # ここで止まってしまう

処理完了まで繰り返し確認するループが必要です。

パターン2:ポーリング間隔が短すぎる

time.sleep(1) のような短い間隔で確認し続けると、APIのレート制限に引っかかります。File APIのリクエスト制限は比較的厳しく、短時間に大量のGET要求を送ると429エラーが返ってきます。

パターン3:ファイル自体が処理できない形式

MIMEタイプの指定ミスや、対応していないコーデックの動画を渡した場合、PROCESSING のまま長時間停滞し最終的に FAILED に遷移します。この場合はいくら待っても ACTIVE にはなりません。

正しいポーリング実装:指数バックオフ付きの待機ループ

実際に使えるポーリング関数です。間隔を徐々に広げる指数バックオフと、全体のタイムアウトを組み合わせています。

import google.generativeai as genai
import time
import math
 
def wait_for_file_active(
    file_name: str,
    max_wait_seconds: int = 300,  # 最大5分待機
    initial_interval: float = 5.0,  # 最初の待機間隔
    max_interval: float = 30.0,    # 最大待機間隔
) -> genai.protos.File:
    """
    ファイルがACTIVE状態になるまで待機する。
    
    Args:
        file_name: アップロードしたファイルのname(例: "files/abc123")
        max_wait_seconds: この時間を超えたらTimeoutErrorを投げる
        initial_interval: 最初のポーリング間隔(秒)
        max_interval: 最大ポーリング間隔(秒)
    
    Returns:
        ACTIVE状態のFileオブジェクト
    
    Raises:
        TimeoutError: max_wait_seconds以内にACTIVEにならなかった場合
        RuntimeError: ファイルがFAILED状態になった場合
    """
    start_time = time.time()
    interval = initial_interval
    attempt = 0
    
    while True:
        elapsed = time.time() - start_time
        
        if elapsed > max_wait_seconds:
            raise TimeoutError(
                f"ファイル {file_name}{max_wait_seconds}秒以内にACTIVEになりませんでした"
            )
        
        file = genai.get_file(file_name)
        attempt += 1
        
        print(f"  [{attempt}] {file.name}: {file.state} ({elapsed:.0f}秒経過)")
        
        if file.state == genai.protos.File.State.ACTIVE:
            print(f"✅ ACTIVE になりました({elapsed:.0f}秒)")
            return file
        
        if file.state == genai.protos.File.State.FAILED:
            raise RuntimeError(
                f"ファイル {file_name} の処理が失敗しました: {getattr(file, 'error', '不明なエラー')}"
            )
        
        # 指数バックオフ:間隔を徐々に広げる
        time.sleep(interval)
        interval = min(interval * 1.5, max_interval)

使い方はシンプルです。

# アップロード
uploaded = genai.upload_file("document.pdf", mime_type="application/pdf")
print(f"アップロード完了: {uploaded.name}")
 
try:
    # ACTIVE になるまで待機
    active_file = wait_for_file_active(uploaded.name, max_wait_seconds=180)
    
    # モデルにリクエスト
    model = genai.GenerativeModel("gemini-2.5-flash")
    response = model.generate_content(["この書類を要約してください。", active_file])
    print(response.text)
 
except TimeoutError as e:
    print(f"タイムアウト: {e}")
    # 必要に応じてファイルを削除
    genai.delete_file(uploaded.name)
 
except RuntimeError as e:
    print(f"処理失敗: {e}")

タイムアウト時のクリーンアップ

タイムアウトや失敗が発生したとき、アップロードしたファイルはGoogleのサーバーに残ります。File APIの保持期限は48時間ですが、大量のファイルを扱う処理では使い終わったファイルを明示的に削除する習慣をつけることをお勧めします。

import contextlib
 
@contextlib.contextmanager
def managed_file(file_path: str, mime_type: str):
    """アップロードしたファイルを自動的にクリーンアップするコンテキストマネージャー"""
    uploaded = genai.upload_file(file_path, mime_type=mime_type)
    try:
        active = wait_for_file_active(uploaded.name)
        yield active
    finally:
        # 正常終了・例外発生どちらの場合でも削除
        try:
            genai.delete_file(uploaded.name)
            print(f"🗑️ ファイル削除: {uploaded.name}")
        except Exception:
            pass  # 削除失敗は無視
 
# 使い方
with managed_file("video.mp4", "video/mp4") as file:
    response = model.generate_content(["動画の内容を説明してください。", file])
    print(response.text)
# withブロックを抜けた時点で自動削除

MIMEタイプのミスで止まる場合

FAILED になるまでに時間がかかるケースの多くは、MIMEタイプの指定が正しくないことが原因です。たとえば .mp4 ファイルを video/mpeg で送ると、処理は開始されますが最終的に失敗します。

Gemini File APIがサポートするMIMEタイプの主な対応は以下の通りです。

  • 動画: video/mp4video/mpegvideo/movvideo/avivideo/webm
  • 音声: audio/mpegaudio/wavaudio/oggaudio/flac
  • 画像: image/jpegimage/pngimage/gifimage/webp
  • ドキュメント: application/pdftext/plaintext/html

ファイルの拡張子からMIMEタイプを自動判定するユーティリティを使うと、ミスが減ります。

import mimetypes
 
def get_mime_type(file_path: str) -> str:
    """ファイルパスからMIMEタイプを判定する"""
    mime_type, _ = mimetypes.guess_type(file_path)
    if mime_type is None:
        raise ValueError(f"MIMEタイプを判定できませんでした: {file_path}")
    return mime_type
 
# 使い方
path = "sample_video.mp4"
mime = get_mime_type(path)  # → "video/mp4"
uploaded = genai.upload_file(path, mime_type=mime)

バッチ処理での並列アップロード

壁紙アプリの画像分類では、多数の画像を一括で処理する必要がありました。ファイルを順番にアップロードして一つずつ待機するのは非効率です。並列アップロードと非同期ポーリングを組み合わせると、処理時間を大幅に短縮できます。

import asyncio
import google.generativeai as genai
 
async def async_wait_for_active(file_name: str, max_wait: int = 300) -> genai.protos.File:
    """非同期版のファイル待機関数"""
    start = asyncio.get_event_loop().time()
    interval = 5.0
    
    while True:
        elapsed = asyncio.get_event_loop().time() - start
        if elapsed > max_wait:
            raise TimeoutError(f"{file_name} がタイムアウト")
        
        # genai.get_file は同期関数なのでexecutorで実行
        loop = asyncio.get_event_loop()
        file = await loop.run_in_executor(None, genai.get_file, file_name)
        
        if file.state == genai.protos.File.State.ACTIVE:
            return file
        if file.state == genai.protos.File.State.FAILED:
            raise RuntimeError(f"{file_name} の処理が失敗")
        
        await asyncio.sleep(min(interval, 30.0))
        interval *= 1.5
 
async def batch_upload_and_wait(file_paths: list[str]) -> list[genai.protos.File]:
    """複数ファイルを並列アップロードし、全て ACTIVE になるまで待機"""
    loop = asyncio.get_event_loop()
    
    # 順番にアップロード(upload_fileは同期)
    uploaded_files = []
    for path in file_paths:
        mime = get_mime_type(path)
        f = await loop.run_in_executor(None, lambda p=path, m=mime: genai.upload_file(p, mime_type=m))
        uploaded_files.append(f)
        print(f"アップロード: {f.name}")
    
    # 全ファイルの完了を並列待機
    active_files = await asyncio.gather(
        *[async_wait_for_active(f.name) for f in uploaded_files]
    )
    return list(active_files)
 
# 実行例
# files = asyncio.run(batch_upload_and_wait(["img1.jpg", "img2.jpg", "img3.jpg"]))

2014年からアプリ開発を続けてきた経験上、バッチ処理のAPIコストは積み重なります。並列化で時間を短縮しつつ、不要なファイルは即座に削除する設計が、長期的な運用コスト削減につながります。

トラブルシューティングチェックリスト

PROCESSING で止まると感じたときに確認する順序です。

ポーリングのループが実装されているか確認してください。一度だけ確認するコードでは必ず止まります。次に、ポーリング間隔が5秒以上あるか見てください。短すぎると429エラーが混入して状況が複雑になります。MIMEタイプが正しいかも確認ポイントです。mimetypes ライブラリで自動判定する方法が確実です。最後に、max_wait_seconds を設定しているか確認してください。無限ループは本番環境で確実に問題を起こします。

File APIの詳しいアップロード手順や対応ファイル形式については、Gemini File API完全ガイドも参照してください。非同期処理に絡むタイムアウト問題全般については、Gemini APIエラーハンドリングガイドが参考になります。

ポーリングの設計を一度きちんと整えてしまえば、File APIはとても使いやすいツールです。長期運用の中で安定して動き続けるコードを目指していきましょう。

同じ問題で詰まっている方の参考になれば幸いです。

シェア

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

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

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

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

関連記事

API / SDK2026-06-12
Gemini API の空応答を finish_reason から逆引きする — 診断フロー・再試行分類・監視の実務
response.text が空になる問題は candidates・prompt_feedback・finish_reason の3層で診断できます。思考トークンによる出力枯渇の検出、再試行可否の分類器、空応答率の監視まで、本番で運用している実装をまとめました。
API / SDK2026-06-14
Gemini API の本文が途中で切れる — finish_reason: MAX_TOKENS を検知して続きを継ぎ直す実装メモ
長文生成で末尾が文の途中でぷつりと切れる。原因の多くは finish_reason: MAX_TOKENS です。例外も 200 のまま静かに混入するこの事故を検知し、続きを継ぎ足して全文を取り戻す実装を、思考トークンの落とし穴とあわせてまとめました。
API / SDK2026-06-01
Gemini 2.5/3 で本文が空なのに finish_reason が MAX_TOKENS になるときの原因と対処
プロンプトはほんの数行なのに、maxOutputTokens を絞った gemini-2.5-flash が空文字を返し finish_reason が MAX_TOKENS になる — 犯人は思考トークンです。原因と3通りの対処を実装コードで整理します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →