◉CLAUDE LABEN
●2.1.311 — セルフホステッドランナーの待機保持オプションを追加し、起動時のフリーズや割り込みの停止を修正しました(10/26)●2.1.307 — スリープやキーチェーンロックのあとに MCP が再接続されない問題を修正しました(10/21)。2.1.306 では claude -p の誤表示も解消●SONNET 4.5 — Claude API での提供終了は 11/30、残り33日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●API — 新しい API バージョン 2026-10-21 では temperature・top_p・top_k を渡すと 400 になります●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.311 — セルフホステッドランナーの待機保持オプションを追加し、起動時のフリーズや割り込みの停止を修正しました(10/26)●2.1.307 — スリープやキーチェーンロックのあとに MCP が再接続されない問題を修正しました(10/21)。2.1.306 では claude -p の誤表示も解消●SONNET 4.5 — Claude API での提供終了は 11/30、残り33日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●API — 新しい API バージョン 2026-10-21 では temperature・top_p・top_k を渡すと 400 になります●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/API & SDK
⬡ API & SDK/2026-10-28中級

web fetch が通らないとき、400 と 200 のどちらで返るかで疑う場所が変わります

web fetch を tools に足した途端に 400 が返るのか、200 のまま結果ブロックに error_code が入るのか。失敗の出方で二層に分けて、組織設定・ドメイン制限・URL の出どころを順に疑う切り分け表と、確かめた Python の診断関数をまとめました。

Claude API137web fetchトラブルシューティング64400エラードメイン制限

Platform のリリースノートを夜に読み返していて、10月20日の項目で手が止まりました。組織の管理者が、Console の Capabilities ページから web fetch を組織ごと無効にできるようになった、という一文です。無効にすると、web fetch ツールを含む Messages API のリクエストは 400 で失敗する、とも書かれていました。

個人開発で小さな調べもの用スクリプトを動かしている私自身は、管理画面を触る人とコードを書く人が同じです。ところが、チームで使う鍵では、その二人が別人になります。そのとき、コードに誤りがないのに 400 が返ってきたら、私は間違いなくツールの type に書いた日付つきの文字列から疑い始めるだろうと思いました。

そこで、公式ドキュメントを読み直して、失敗の出方を整理しました。気づいたのは、web fetch の失敗は出方が二層に分かれていて、疑う場所がまったく違うのだということでした。いま思えば、400 という数字だけを手がかりにしていた私の読み方が浅かったのかもしれません。

最初にお伝えしたいのは、組織側の設定で web fetch が切られている場合、コードに誤りがなくても 400 になる、という点です。

失敗の出方は二層ある

公式の説明を整理すると、次のようになります。

層見え方意味するもの
リクエスト全体HTTP 400、invalid_request_error。メッセージに web fetch が有効でない旨が出る組織の管理者が Console の Capabilities ページで web fetch を無効にしている
結果ブロックHTTP 200 のまま、web_fetch_tool_result_error に error_code が入るツールは使えたが、その URL の取得が通らなかった

後者は 200 で返るため、例外処理だけを書いていると失敗に気づかないまま会話が続きます。Claude は結果ブロックのエラーを受け取って、そのターンを続けるからです。画面には「取得できませんでした」という言い回しの文章が出るだけで、プログラムの側は成功扱いになります。

結果ブロックの error_code と、最初に見る場所

ドキュメントに載っているコードを、見る場所の順に並べ直しました。

error_code起きていること最初に見る場所
url_not_allowedドメイン規則、組織の設定、Anthropic 側の制限(プライベートアドレス、robots.txt、認証情報らしい文字列を含む URL)のいずれかで拒否allowed_domains / blocked_domains と、組織側のリスト。両方を通る必要があります
url_not_in_prior_contextその URL が会話の中に先に出てきていない、または url_sources で除外した出どころにしか出ていないURL を user メッセージに入れたか、許可した tool_result に含まれているか
url_not_accessible取得先の HTTP エラーURL を手元のブラウザで開く
max_uses_exceededmax_uses の上限超え。失敗した取得も数に入る上限値と、同じ URL の再試行回数
unsupported_content_typetext / HTML / PDF 以外画像や zip を指していないか
invalid_tool_input / url_too_longURL の形式不正、または 250 文字超えクエリ文字列が長すぎないか
too_many_requests / unavailableレート制限、内部エラー時間を置いて再試行

なかでも見落としやすいのが url_not_in_prior_context です。URL が Claude 自身の出力にしか出てこない場合や、system prompt にしかない場合は、取得できません。system prompt の URL を取らせたいときは、同じ URL を user メッセージにも入れる必要があります。

切り分けを関数にしておく

毎回目で読むのをやめて、失敗の層を判定する小さな関数を置くことにしました。例外は BadRequestError で受け、200 の場合は結果ブロックを走査します。

#!/usr/bin/env python3
"""web fetch の失敗を「リクエスト全体の 400」と「結果ブロックの error_code」に分けて読む。"""
import anthropic
 
# 結果ブロック (HTTP 200) の error_code ごとに、最初に見る場所を決めておく
NEXT_STEP = {
    "url_not_allowed": "allowed_domains / blocked_domains と、組織の許可・ブロックの両方を見る",
    "url_not_in_prior_context": "その URL が user メッセージか、許可した tool_result に入っているか確認する",
    "url_not_accessible": "相手先の HTTP エラー。URL を手で開いて確かめる",
    "max_uses_exceeded": "max_uses を見直す。失敗した取得も数えられる",
    "unsupported_content_type": "text / HTML / PDF 以外は取れない",
    "invalid_tool_input": "URL の綴りとスキーム (http / https) を確認する",
    "url_too_long": "URL が 250 文字を超えている",
    "too_many_requests": "少し待ってから再試行する",
    "unavailable": "内部エラー。時間を置いて再試行する",
}
 
def triage(call):
    """call は引数なしで Messages API を呼ぶ関数。返り値は (層, 内容)。"""
    try:
        resp = call()
    except anthropic.BadRequestError as e:
        msg = str(getattr(e, "message", e))
        low = msg.lower()
        if "web fetch" in low and "not enabled" in low:
            return ("org-setting", "組織側で web fetch が無効。Console の Capabilities ページを管理者に確認する")
        return ("other-400", msg[:200])  # temperature など別の 400 はここへ落とす
    found = []
    for b in getattr(resp, "content", []) or []:
        if getattr(b, "type", "") != "web_fetch_tool_result":
            continue
        c = getattr(b, "content", None)
        if getattr(c, "type", "") == "web_fetch_tool_result_error":
            code = getattr(c, "error_code", "?")
            found.append((code, NEXT_STEP.get(code, "公式のエラー一覧を確認する")))
    return ("result-error", found) if found else ("ok", None)

使い方は、triage(lambda: client.messages.create(...)) と包むだけです。

この関数を書いた理由は二つあります。一つは、400 の文言に頼りすぎないためです。文言の一致で組織設定と判定しているのは web fetch と not enabled の二語だけで、それ以外の 400 は other-400 に落とします。もう一つは、200 の中の失敗を、ログに必ず一行残すためです。

確かめられたことと、確かめられていないこと

正直に書き残しておきます。私の手元の組織では web fetch を無効にしておらず、組織設定による 400 は自分では再現できていません。この層の説明は、公式ドキュメントの記述に沿ったものです。

確かめたのは次の範囲です。SDK の BadRequestError を擬似的に作り、「web fetch is not enabled」を含む文言なら org-setting、別の文言なら other-400 に振り分けられること。結果ブロックに url_not_allowed を持たせた応答なら result-error になり、エラーのない応答なら ok になること。四通りとも、期待どおりの値が返りました。

つまり、判定の枝は動作を確かめましたが、本物の 400 の文言は確かめていません。本番に入れるなら、組織設定を切り替えられる検証用の環境で、一度だけ実物の文言を見ておくことをお勧めします。

二層を踏まえた確認の順番

いまは、web fetch が通らないときに次の順で見ています。

  1. 400 か 200 かを見ます。400 で web fetch に触れる文言があれば、コードではなく組織の設定を管理者に聞きます
  2. 200 なら、結果ブロックの error_code を一覧に当てます
  3. url_not_allowed なら、リクエスト側のドメイン指定と組織側のリストの両方を見ます
  4. url_not_in_prior_context なら、URL がどこから会話に入ったかを辿ります

2 と 3 の関係は、取り違えやすいところです。ドキュメントには、組織の許可・ブロックの設定とツール側の allowed_domains / blocked_domains の両方を通る必要がある、と書かれています。ツール側で通した URL でも、組織側で外れていれば url_not_allowed になるのです。

関連して、web fetch の設計そのものは Claude API の Web Fetch ツールで最新情報を取り込む設計 に、別の 400 の読み方は invalid_request_error の文言から原因を引く に書いています。同じ 400 でも、API バージョン由来のものは temperature 系が 400 になる前に呼び出し箇所を洗った記録 のほうが近い話です。

結びに

失敗は、HTTP の層で一度分けてから、コードを疑います。 この線引きだけは、疲れている夜でも守るようにしています。

まずは手元の呼び出しを triage で包み、200 の中の error_code をログに一行出すところから始めていただければと思います。何も出なければ、それは「通っている」という確認になります。

シェア

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

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

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

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

関連記事

⬡ API & SDK2026-10-24
「Credit balance is too low」の前に見る3つの勘定 — 含まれるクレジットと購入分は別の財布です
Max や Team の API クレジットが残っているはずなのに「Credit balance is too low」と出る。原因は、含まれるクレジットの対象外の呼び出しと、購入分の残高、月間上限が別々に動いているためです。Console で見る順番と、夜間スクリプトに入れる検知コードをまとめました。
⬡ API & SDK2026-10-22
上限に達したのに 400 が返ってきた — 自分で決めた利用上限・階層の月間上限・レート制限を見分ける
Claude API の「上限」系エラーは3種類あり、ステータスも待てば直るかどうかも違います。自分で設定した上限は400、階層の月間上限は429、通常のレート制限も429。見分けるコードと判断表をまとめました。
⬡ API & SDK2026-09-02
thinking ブロックを弾く 400 が出たとき、会話履歴のどこが書き換わっていたか
Claude Fable 5.1 から、thinking ブロックより前の system・tools・messages を書き換えたリクエストが 400 で弾かれるようになりました。自分のエージェントループが該当するかを1リクエストで確かめる手順と、system の組み立て直し・履歴の切り詰めを書き換えなしのやり方へ移す実装をまとめます。