【生成AI】Gemini APIで突然の「403 Forbidden」...犯人は「過去のファイル」だった!?

※本記事は、実際に発生した開発トラブルの解決事例に基づき、構成および執筆に生成AIを使用しています。記載内容は技術者による実機検証・レビュー済みですが、ご利用の際は環境に合わせて十分な検証を行ってください。

こんにちは!話題のGemini APIを使って、チャットbotに最新の資料を読み込ませるRAG(検索拡張生成)の仕組みを作っていたときのお話です。

テスト中は順調に動いていたのに、デプロイして数日経つと急に**「You do not have permission to access the File b9wgpsu84tg1」**という 403 Forbidden の壁にぶち当たってしまいました。

今回はその「403エラーの正体」と、「どうやって解決したのか」をシェアしたいと思います。

事象:数日前は動いていたのに急にAPIから怒られる

Botの仕組みは簡単で、PDFなどの資料をGeminiの File API (アップロード) に投げ込んでおき、LINEからメッセージが来るたびにその「ファイルID」と一緒にプロンプトを投げて賢く答えてもらう、というものでした。

ところが、数日放置した後にふとメッセージを送ってみると、ログには以下のような悲しいエラーが……。

[GoogleGenerativeAI Error]: Error fetching from https://generativelanguage.googleapis.com/...
[403 Forbidden] You do not have permission to access the File b9wgpsu84tg1 or it may not exist.

「あれ?APIキーを書き換えたっけ…?」と思いましたが、キーは合っているのに何度やっても403で怒られます。

原因:Geminiのファイルは「賞味期限が2日」!

原因はGeminiの公式ドキュメントにひっそりと(でも重要に)書かれていた、ファイルの保管仕様でした。

API経由でアップロードされたファイルは、永続的に保存されるわけではなく、アップロードから48時間(2日間)経つと自動的に「有効期限切れ」となって削除されるのです。

私たちのBotでは、データベース(Supabase等)に「アップロードできたよ!」というステータス(例:completed)のまま、2日以上経ったファイルのIDを律儀に保存しっぱなしにしていました。 LINEのメッセージが来るたびに、その「すでに消え去った古いファイルID」もご丁寧にコンテキストとしてGeminiに投げてしまっていたため、Gemini側が「そんなファイル、もうない(あるいは権限がない)!」と即座に403エラーを返していたわけです。

解決策:使うのは最新だけ!&送る前に「生存確認」

この罠を回避するために、RAGの取得ロジックに以下の対応を入れました。

  1. DBから取得するファイルを最新1件に絞る 過去のアップロード記録を全部渡すのをやめました。
const { data: files } = await supabase
  .from("uploaded_files")
  .eq("status", "completed")
  .order("created_at", { ascending: false })
  .limit(1); // 最新だけ使う!
  1. Geminiに投げる前にファイルの「生存確認」をする 念には念を入れて、いざそのIDを使う直前に「まだ生きてますか?」とREST APIで確認し、404や403ならスキップするようにしました。
const checkRes = await fetch(`${fileUrl}?key=${apiKey}`);
if (checkRes.ok) {
  // 生きているならコンテキストに追加
  fileParts.push({ fileData: { ... } });
} else {
  console.warn(`ファイルはもう存在しませんでした。テキストだけで続行します!`);
}

これで、時間が経って期限切れになったファイルを参照してしまっても、エラーを回避しつつ安全にテキストだけで返信できるようになりました!

生成AIの「ファイル保管」には意外に厳しい寿命があること、みなさんもお忘れなきように!

※本記事は、実際に発生した開発トラブルの解決事例に基づき、構成および執筆に生成AIを使用しています。記載内容は技術者による実機検証・レビュー済みですが、ご利用の際は環境に合わせて十分な検証を行ってください。