冒頭まとめ
GCP の 404 Not Found は、指定した対象が見つからないことを示します。素直な意味に見えますが、このエラーには「権限が無い場合に、存在を隠すため 404 が返される」という広く知られた説明があり、どこまで本当なのかが調べ方を左右します。
Google が公開しているエラー区分の定義ファイルには、実装する側への注意書きが添えられています。段階的な機能の公開や、公開されていない許可名簿のように、利用者の層ごと拒否する場合には 404 の区分を使ってよい。しかし、利用者単位のアクセス制御のように、層の中の一部の利用者だけを拒否する場合には、403 の区分を使わなければならない。こう書かれています。原則としては、権限の設定による拒否は 403 です。
一方で、運用上の公式文書には別の記述もあります。権限の無い利用者に対象の存在を明かさないために、403 の代わりに 404 が返ることがある、と明記されています。つまり「404 は必ず存在の問題」とまでは言えません。
それでも、調べる順序は変わりません。同じ公式文書が勧めているのは、まず識別子と経路を確認し、対象が実在するかを確かめ、それでも解決しなければ裏に認可の問題がないかを考える、という順序です。名前、プロジェクト、場所の3つを先に確かめれば、たいていの 404 はそこで原因に行き着きます。権限の側を調べるのは、その後です。
なお、403 の側の文言には「あるいは対象が存在しない可能性があります」という但し書きが付きます(GCP の 403 の記事)。403 と 404 は、存在と権限の両面で互いに染み出し得る隣どうしの区分だ、と押さえておくのが正確です。
エラーの概要
応答は他のエラーと共通の形で、status に区分名が入ります。
{
"error": {
"code": 404,
"message": "The resource 'projects/my-project/zones/asia-northeast1-a/instances/my-vm' was not found",
"status": "NOT_FOUND"
}
}
読むべきは message に含まれる対象の名前です。上の例では、どのプロジェクトの、どの場所の、どの対象を探したかが完全な形で書かれています。自分が指定したつもりの内容と、ここに出ている内容を並べれば、食い違いはすぐ見つかります。
特に見落としやすいのが場所の部分です。指定を省略した場合、道具の設定に入っている既定値が使われます。その既定値が意図と違っていると、正しい名前を指定しているのに見つからない、という状態になります。
コマンド行の道具からは、同じ内容が簡潔な形で出ます。
ERROR: (gcloud.compute.instances.describe) Could not fetch resource:
- The resource 'projects/my-project/zones/asia-northeast1-a/instances/my-vm' was not found
まず最初に:応答に出ている完全な名前と、自分の指定を並べる
第一に、message に出ている対象の完全な名前を読みます。ここには、実際に探しに行った先がそのまま書かれています。
第二に、いま道具が使っている既定値を確認します。
gcloud config list
プロジェクト、地域、場所の既定値がここに出ます。応答に出ていた名前と食い違っていれば、それが原因です。
第三に、対象が本当に存在するかを一覧で確認します。個別に問い合わせるのではなく、一覧を取得して目で確かめるほうが確実です。
第四に、status を確認します。NOT_FOUND であれば、まず存在の問題として調べます。PERMISSION_DENIED が返っているなら、これは 404 ではなく 403 なので、調べる先が変わります。
よくある原因と解決手順
原因1:場所の指定が抜けている、または既定値と食い違う
最も多い形です。多くの対象は場所ごとに管理されているため、場所が違えば見つかりません。
Before(場所を指定せず、既定値に任せる):
gcloud compute instances describe my-vm
# → 設定の既定値の場所を探しに行く
After(場所を明示する):
gcloud compute instances describe my-vm --zone=asia-northeast1-b
どこにあるか分からない場合は、一覧を取得すれば場所ごと分かります。
gcloud compute instances list --format="table(name, zone, status)"
対象の種類によって、場所の単位が違う点にも注意が要ります。地域単位のもの、より細かい単位のもの、場所を持たないものがあります。指定する引数の名前が違うので、種類ごとに確認してください。
原因2:プロジェクトが違う
複数のプロジェクトを扱っている場合に起きます。名前も場所も正しいのに、探しているプロジェクトが違います。
# 現在の既定値を確認する
gcloud config get-value project
# 一時的に別のプロジェクトを指定する
gcloud compute instances describe my-vm --zone=asia-northeast1-b --project=other-project
自動化の中で起きる場合は、実行環境の設定が手元と違うことを疑ってください。手元では通るのに自動化では 404 になる、という現象の多くはこれです。
原因3:名前の綴りが違う
対象の名前は大文字と小文字が区別されます。また、横棒と下線の取り違えも起きます。
一覧を取得して、実際の名前と見比べるのが確実です。推測で修正すると、別の綴りで再び失敗します。
gcloud compute instances list --format="value(name)" | grep -i my
大文字と小文字を無視して探せば、綴りの違いだけの対象が見つかります。
原因4:作成直後で、まだ見えていない
対象を作った直後に問い合わせると、一時的に見つからないことがあります。作成の処理が完了していない段階です。
Before(作成後すぐに問い合わせる):
gcloud compute instances create my-vm --zone=asia-northeast1-b
gcloud compute instances describe my-vm --zone=asia-northeast1-b
# → 稀に 404 になる
After(操作の完了を待ってから問い合わせる):
gcloud compute instances create my-vm --zone=asia-northeast1-b
gcloud compute operations list --filter="status!=DONE" --format="value(name)"
# 完了を確認してから次へ進む
時間を置いて再試行する形でも構いませんが、待つ秒数を決め打ちにすると、遅れたときに失敗します。操作の状態を確認するほうが確実です。
なお、この形は 404 の中では例外的に、待てば解消する種類です。他の原因はすべて、指定を直さない限り変わりません。
原因5:呼び出し先の経路が違う
窓口を直接叩いている場合に起きます。版の指定や、経路の組み立てが実際の仕様と違っていると、対象ではなく経路そのものが見つかりません。
この場合、message に出る内容が対象の名前ではなく、経路に関するものになります。応答の中身を見れば区別できます。
対処は、公式の一覧で経路の形を確認することです。同じサービスでも版によって形が変わるため、古い記事を参考にすると食い違います。
原因6:権限の側で存在が隠されている
名前、プロジェクト、場所、経路をすべて確認しても見つからない場合に、最後に疑う形です。公式文書には、対象の存在を明かさないために 403 の代わりに 404 が返ることがあると書かれています。
この場合、自分の指定をいくら直しても変わりません。対象を管理している側に、その名前の対象が実在するか、自分の身元に閲覧の権限があるかを確認してください。調べる内容は 403 と同じになります(GCP の 403 の記事)。
補足:似ているが別のもの
権限が足りない場合は、原則として 403 です。利用者単位のアクセス制御による拒否には 403 を使わなければならないと定義に明記されています(GCP の 403 の記事)。ただし前述のとおり、存在の秘匿のために 404 が使われる場合があることも公式文書に書かれています。403 の文言に「あるいは対象が存在しない可能性があります」と付くのと合わせて、この2つの区分は境界の両側で互いに染み出します。
認証が通っていない場合は 401 です(GCP の 401 の記事)。送った内容そのものに問題がある場合は 400 で、区分が3つに分かれます(GCP の 400 の記事)。
対象がまだ作成中である場合、404 ではなく 400 の系統になることもあります。status が FAILED_PRECONDITION であれば、対象は存在するが状態が整っていない、という意味です。
処理が内部で失敗した場合は 500、一時的に処理できない場合は 503 です(GCP の 500 の記事、503 の記事)。
切り分けの順序
messageに出ている対象の完全な名前を読む。実際に探しに行った先がそこに書かれている。- 道具の既定値を確認する。プロジェクトと場所が意図どおりか。
- 一覧を取得して、対象が実在するかを確かめる。個別の問い合わせより確実。
- 名前は大文字と小文字を区別する。大小を無視した検索で綴り違いを探す。
- 作成直後なら、操作の完了を確認してから問い合わせる。この場合だけ待てば解消する。
- 窓口を直接叩いているなら、版と経路を公式の一覧で確認する。
- ここまでで解決しなければ、権限の側を確認する。存在の秘匿のために 404 が返る場合があると公式文書にも書かれている。調べる内容は 403 と同じ。
確認コマンド集
# 1. 道具の既定値をまとめて確認する
gcloud config list
# 2. 対象の一覧を取得して、名前と場所を確かめる
gcloud compute instances list --format="table(name, zone, status)"
gcloud storage buckets list --format="value(name)"
gcloud sql instances list --format="table(name, region)"
# 3. 大文字と小文字を無視して名前を探す
gcloud compute instances list --format="value(name)" | grep -i <一部の名前>
# 4. 応答の status と対象名を取り出す
curl -sS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://<サービス>.googleapis.com/v1/<資源>" | python3 -c "
import json,sys
d=json.load(sys.stdin)['error']
print(d['code'], d['status'])
print(d['message'])
"
# 5. 進行中の操作を確認する(作成直後の場合)
gcloud compute operations list --filter="status!=DONE"
# 6. 送受信の内容をそのまま見る(経路を確認する)
gcloud <サービス> <操作> --log-http 2>&1 | grep -i "^uri\|^-- request" | head
Editor’s Note
「権限が無いと 404 が返る」という説明は、GCP に限らず広く見られます。GCP の場合、この説明には二層の答えがあります。
定義の側の注意書きは、実装する側に向けて書かれています。層ごと拒否する場合には 404 を使ってよく、層の中の一部の利用者を拒否する場合には 403 を使わなければならない、という指示です。前者の例として挙げられているのは、段階的な機能の公開と、公開されていない許可名簿です。どちらも「あなたには、この機能そのものが存在しない」という状況で、隠しているのは対象ではなく機能の存在です。
一方、運用の側の公式文書は、存在を明かさないために 403 の代わりに 404 が返ることがある、と率直に認めています。定義の原則と運用の実態には、幅があるということです。ただし同じ文書が勧める調べ方は、識別子と経路をまず確認し、それでも解決しなければ認可の問題を考える、という順序です。本記事の切り分けの順序が権限を最後に置いているのは、この助言と同じ理由によります。ほとんどの 404 は、名前、プロジェクト、場所のどれかで説明が付くからです。
経路の側の 404 には、実際の記録もあります。Cloud Run のジョブ(当時は公開の試用段階)を作成しようとして 404「Requested entity was not found」が返り続けた報告(pulumi/pulumi-google-native Issue #639)では、失敗した要求は v2 の経路に送られており、スレッド内の検討は、当時の公式リファレンスで作成の手順が v1 側に置かれていた食い違いに向かっています。対象の名前をいくら見直しても、経路の版が違えば見つからない——原因5の形そのものです。
404 に当たったら、名前、プロジェクト、場所。この3つから確かめてください。権限の話は、それでも残った場合の最後の1つです。
免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。
この記事でエラーは解決しましたか?