【生成AI】Gemini APIで突然の「403 Forbidden」...犯人は「過去のファイル」だった!?
こんにちは!話題の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の取得ロジックに以下の対応を入れました。
- DBから取得するファイルを最新1件に絞る 過去のアップロード記録を全部渡すのをやめました。
const { data: files } = await supabase
.from("uploaded_files")
.eq("status", "completed")
.order("created_at", { ascending: false })
.limit(1); // 最新だけ使う!
- Geminiに投げる前にファイルの「生存確認」をする 念には念を入れて、いざそのIDを使う直前に「まだ生きてますか?」とREST APIで確認し、404や403ならスキップするようにしました。
const checkRes = await fetch(`${fileUrl}?key=${apiKey}`);
if (checkRes.ok) {
// 生きているならコンテキストに追加
fileParts.push({ fileData: { ... } });
} else {
console.warn(`ファイルはもう存在しませんでした。テキストだけで続行します!`);
}
これで、時間が経って期限切れになったファイルを参照してしまっても、エラーを回避しつつ安全にテキストだけで返信できるようになりました!
生成AIの「ファイル保管」には意外に厳しい寿命があること、みなさんもお忘れなきように!