【Bug已解决】API Error: 400 invalid beta flag when trying to use Claude Code with Bedrock using claude 3.5 haiku 解决方案

一、现象长什么样

你用 Claude Code(或 anthropic SDK)通过 AWS Bedrock 调用 Claude 3.5 Haiku,却收到:

  • API Error: 400 invalid beta flag
  • 400 {"type":"error","error":{"type":"invalid_request_error","message":"invalid beta flag"}}
  • 在直连 api.anthropic.com 时一切正常,一旦切到 Bedrock 就报这个;
  • 你并没主动设置任何 beta,但 Claude Code 默认会带一些 anthropic-beta 头(比如为 prompt caching、或某个预览特性);
  • 报错只在特定模型/特定调用路径出现,比如启用了某个需要 beta 的能力(computer use、extended thinking 等)时。

一句话:Claude Code / SDK 默认携带的 anthropic-beta 请求头,Bedrock 的 InvokeModel 通道不认识,于是返回 400 invalid beta flag。

二、背景

Anthropic 用 anthropic-beta 请求头来开启"预览/实验性"特性(如 prompt-caching-2024-07-31max-tokens-3-5-sonnet-2024-07-15)。直连 Anthropic 官方 API 时,带上这些头是合法的,服务端会按 beta 处理。

但当你通过 AWS Bedrock 调用时,请求是走 Bedrock 的 InvokeModel / Converse 通道,由 Bedrock 再做一层转发。Bedrock 不接受、也不会透传 anthropic-beta 头——它只认 Bedrock 自己的头(如 x-amzn-bedrock-*),而把 anthropic-beta 视为非法字段,返回 400 invalid beta flag。

Claude Code 这类工具为了统一体验,默认可能在请求里塞 beta 头;一旦目标后端是 Bedrock,这些头就成了毒药。

三、根因

根因是同一套客户端代码对"官方 endpoint"和"Bedrock endpoint"发送了相同的 anthropic-beta 头,而 Bedrock 不接纳它

# 伪代码:客户端无条件带 beta 头
headers = {
    "x-api-key": key,
    "anthropic-version": "2023-06-01",
    "anthropic-beta": "prompt-caching-2024-07-31",   # 直连 OK,Bedrock 报错
}
# 当 base_url 指向 Bedrock 网关时,这个头导致 400

修复方向:当后端是 Bedrock 时,移除 anthropic-beta(Bedrock 上对应能力通过 Bedrock 自己的方式开启,不需要这个头)。

四、最小可运行复现

from dataclasses import dataclass
from typing import Dict, Optional


def build_headers(base_url: str, beta: Optional[str] = "prompt-caching-2024-07-31") -> Dict[str, str]:
    h: Dict[str, str] = {"x-api-key": "k", "anthropic-version": "2023-06-01"}
    if beta:
        h["anthropic-beta"] = beta
    return h


def simulate_bedrock_validation(headers: Dict[str, str]) -> None:
    # Bedrock 网关只接受白名单头
    allowed = {"x-api-key", "anthropic-version", "content-type"}
    for k in headers:
        if k == "anthropic-beta":
            raise ValueError("400 invalid beta flag")   # Bedrock 拒绝
        if k not in allowed and k.startswith("anthropic-"):
            raise ValueError("400 invalid beta flag")


def main():
    direct = build_headers("https://api.anthropic.com")
    print("direct:", direct)                  # 带 beta,直连 OK
    try:
        simulate_bedrock_validation(direct)
    except ValueError as e:
        print("bedrock ERR:", e)              # 400 invalid beta flag


if __name__ == "__main__":
    main()

运行后直连带 beta 正常,Bedrock 校验抛 400 invalid beta flag,与真实 bug 一致。

五、解决方案(第一层:最小直接修复)

最小修复:当后端是 Bedrock 时,不发送 anthropic-beta 头:

import os
from anthropic import AnthropicBedrock

client = AnthropicBedrock(
    aws_access_key=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_key=os.environ["AWS_SECRET_ACCESS_KEY"],
    aws_region=os.environ["AWS_REGION"],
    # 关键:Bedrock 模式不要带 beta 头
)

# 若用底层 requests,手动去掉 beta 头
headers = {k: v for k, v in headers.items() if k != "anthropic-beta"}

在 Claude Code 里,确保它处于"Bedrock 模式"(通过环境变量或配置指向 Bedrock),让它别再附 beta 头;某些版本需要在配置里显式关掉预览特性。

六、解决方案(第二层:结构化改进)

把"按后端类型决定头集合"抽成策略,作为唯一事实来源:

from dataclasses import dataclass, field
from typing import Dict, Optional, Set


@dataclass(frozen=True)
class ClaudeBedrockBetaFlagPolicy:
    """按后端类型决定请求头:Bedrock 禁止 anthropic-beta。

    规则:
    - 直连 Anthropic:可带 anthropic-beta(用于开启预览特性)
    - Bedrock / Vertex:移除 anthropic-beta,避免 400 invalid beta flag
    """
    backend: str = "bedrock"   # 'anthropic' | 'bedrock' | 'vertex'
    beta_flags: tuple = ("prompt-caching-2024-07-31",)

    def headers(self, api_key: str) -> Dict[str, str]:
        h: Dict[str, str] = {
            "x-api-key": api_key,
            "anthropic-version": "2023-06-01",
            "content-type": "application/json",
        }
        if self.backend == "anthropic":
            h["anthropic-beta"] = ",".join(self.beta_flags)
        # bedrock / vertex 不加 beta 头
        return h

    def sanitize(self, raw: Dict[str, str]) -> Dict[str, str]:
        # 通用护栏:Bedrock 模式下强制剔除 beta 头
        if self.backend in ("bedrock", "vertex"):
            return {k: v for k, v in raw.items() if k != "anthropic-beta"}
        return raw


def demo() -> None:
    b = ClaudeBedrockBetaFlagPolicy(backend="bedrock")
    print(b.headers("k"))          # 无 anthropic-beta
    assert "anthropic-beta" not in b.headers("k")
    a = ClaudeBedrockBetaFlagPolicy(backend="anthropic")
    assert "anthropic-beta" in a.headers("k")


if __name__ == "__main__":
    demo()

七、解决方案(第三层:断言 / CI 守护)

import pytest
from your_module import ClaudeBedrockBetaFlagPolicy


def test_bedrock_has_no_beta_header():
    policy = ClaudeBedrockBetaFlagPolicy(backend="bedrock")
    assert "anthropic-beta" not in policy.headers("k")


def test_anthropic_keeps_beta_header():
    policy = ClaudeBedrockBetaFlagPolicy(backend="anthropic")
    assert "anthropic-beta" in policy.headers("k")


def test_sanitize_strips_on_bedrock():
    policy = ClaudeBedrockBetaFlagPolicy(backend="bedrock")
    sanitized = policy.sanitize({"x-api-key": "k", "anthropic-beta": "x"})
    assert "anthropic-beta" not in sanitized


def test_vertex_also_strips_beta():
    policy = ClaudeBedrockBetaFlagPolicy(backend="vertex")
    assert "anthropic-beta" not in policy.headers("k")

把这些测试加进客户端适配层 CI,确保任何 Bedrock/Vertex 路径都不会再发送 anthropic-beta

八、排查清单

  • 你用的是 Bedrock 还是直连 Anthropic?Bedrock 不接受 anthropic-beta
  • 请求里是否带了 anthropic-beta 头?在 Bedrock 模式下删除它。
  • Claude Code 是否处于 Bedrock 模式?确认配置指向 Bedrock 网关。
  • 报错是否随"启用某预览特性"而出现?那是 beta 头触发。
  • 直连正常、Bedrock 报错,基本可锁定是 beta 头差异。
  • 是否在 CI 守护了"Bedrock 路径不得带 beta 头"?

九、小结

通过 Bedrock 调用 Claude 时收到 400 invalid beta flag,是因为 Claude Code / SDK 默认携带的 anthropic-beta 头在 Bedrock 通道不被接受(Bedrock 只认自己的头)。直连 Anthropic 时这个头合法,切到 Bedrock 就成了非法字段。最小修复是 Bedrock 模式下移除 anthropic-beta 头;结构化做法是抽成 ClaudeBedrockBetaFlagPolicy,按后端类型决定头集合;最后用 pytest 锁死"Bedrock/Vertex 路径不得带 beta 头",避免切换后端时再次踩坑。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐