【Bug已解决】Claude API error despite showing that it‘s working in Anthropic Console 解决方案
【Bug已解决】Claude API error despite showing that its working in Anthropic Console 解决方案一、现象长什么样你在 Anthropic Console 网页里用同一个 API key 调用 Claude一切正常但一落到代码里用anthropicPython/TS SDK或裸requests就报错401 Unauthorized/AuthenticationError: invalid x-api-key400 Bad Request伴随missing required header之类偶尔404或invalid model最迷惑的是Console 能跑、key 也是同一个curl 却失败有时本地能跑、部署到服务器就 401但 key 明明复制过去了还有人用代理式封装把请求转到别的 endpoint后报错但那属于另一类问题。一句话API 调用在代码里失败、Console 里却正常几乎总是请求构造 / 鉴权头 / key 形态与 Console 不一致而不是 key 本身无效。二、背景Anthropic API 的鉴权与请求格式有固定约定请求头必须是x-api-key: 你的key同时anthropic-version: 2023-06-01旧版也接受Authorization: Bearer key但 SDK 默认走x-api-key默认 endpoint 是https://api.anthropic.com/v1/messagesbody 必填model、max_tokens、messagesConsole 网页在后台帮你填好了这些头、版本、endpoint 和正确的 key所以在 Console 能跑不代表你的代码也构造对了。代码里失败的常见元凶key 末尾带了换行/空格、用了Authorization: Bearer但服务端期望x-api-key、把anthropic-version头漏了、或model名写错如claude-3-5-sonnet旧别名 vs 新版claude-3-5-sonnet-latest。三、根因根因是代码侧请求与 Console 生成的请求在鉴权/头上不一致# 错误示范 1key 带了换行 import os api_key os.environ[ANTHROPIC_API_KEY] \n # 复制粘贴带入的换行 # 错误示范 2只设了 Authorization没设 x-api-key / anthropic-version requests.post( https://api.anthropic.com/v1/messages, headers{Authorization: fBearer {api_key}}, # 老习惯可能不被接受 json{...}, ) # 错误示范 3漏掉 anthropic-version requests.post(..., headers{x-api-key: api_key}, json{...}) # 缺 version 头更隐蔽的是环境变量在不同 shell/部署里取值不一致本地.env有 key、服务器没注入于是服务器用空 key 请求401。但因为没有显式报错key 为空看起来就像key 失效。四、最小可运行复现import os import requests def call_claude_raw(api_key: str, model: str claude-3-5-sonnet-latest) - dict: # 正确的最小请求构造 resp requests.post( https://api.anthropic.com/v1/messages, headers{ x-api-key: api_key.strip(), # 注意 strip 掉换行/空格 anthropic-version: 2023-06-01, content-type: application/json, }, json{ model: model, max_tokens: 256, messages: [{role: user, content: hi}], }, timeout30, ) return resp.status_code, resp.text def main(): key os.environ.get(ANTHROPIC_API_KEY, ) # 模拟复制粘贴带入换行导致 401 bad_key key \n print(with newline:, call_claude_raw(bad_key)[:1]) # 很可能 401 print(stripped :, call_claude_raw(key)[:1]) # 正常 if __name__ __main__: main()运行可复现key 带换行 - 401strip 后正常。五、解决方案第一层最小直接修复最小修复是确保请求头与 key 形态和 Console 一致import os from anthropic import Anthropic # 用官方 SDK 最省心它自动加 x-api-key / anthropic-version client Anthropic(api_keyos.environ[ANTHROPIC_API_KEY].strip()) msg client.messages.create( modelclaude-3-5-sonnet-latest, max_tokens256, messages[{role: user, content: hi}], ) print(msg.content)若用裸requests务必headers { x-api-key: api_key.strip(), anthropic-version: 2023-06-01, content-type: application/json, }并确认api_key没有被换行/空格污染从环境变量读取后strip()。六、解决方案第二层结构化改进把API 调用前置校验抽成策略统一处理 key 清洗、头构造、endpoint 选择from dataclasses import dataclass, field from typing import Dict, Optional dataclass(frozenTrue) class ClaudeApiConsoleMismatchPolicy: Claude API 调用策略统一鉴权头与 key 清洗消除 Console 能跑代码不能。 规则 - api_key 自动 strip 换行/空格 - 默认用 x-api-key anthropic-version与 Console 一致 - endpoint / model 给出默认值避免漏填 endpoint: str https://api.anthropic.com/v1/messages anthropic_version: str 2023-06-01 default_model: str claude-3-5-sonnet-latest def clean_key(self, raw: str) - str: if not raw: raise ValueError(API key 为空请检查环境变量是否注入) return raw.strip() def headers(self, api_key: str) - Dict[str, str]: return { x-api-key: self.clean_key(api_key), anthropic-version: self.anthropic_version, content-type: application/json, } def build_body(self, prompt: str, *, model: Optional[str] None, max_tokens: int 256) - Dict: return { model: model or self.default_model, max_tokens: max_tokens, messages: [{role: user, content: prompt}], } def demo() - None: policy ClaudeApiConsoleMismatchPolicy() print(policy.headers(sk-ant-xxx\n)) # key 被清洗 print(policy.build_body(hi)) if __name__ __main__: demo()这样无论本地还是服务器只要 key 注入正确请求构造完全一致不会再出现Console 能、代码不能。七、解决方案第三层断言 / CI 守护import os import pytest from your_module import ClaudeApiConsoleMismatchPolicy def test_key_stripped(): policy ClaudeApiConsoleMismatchPolicy() assert policy.clean_key( sk-x \n) sk-x def test_empty_key_rejected(): policy ClaudeApiConsoleMismatchPolicy() with pytest.raises(ValueError): policy.clean_key() def test_headers_well_formed(): policy ClaudeApiConsoleMismatchPolicy() h policy.headers(sk-x) assert h[x-api-key] sk-x assert h[anthropic-version] 2023-06-01 assert content-type in h def test_body_has_required_fields(): policy ClaudeApiConsoleMismatchPolicy() body policy.build_body(hi, modelclaude-3-5-sonnet-latest) assert body[model] and body[max_tokens] and body[messages]CI 里再加一条从 secret 注入 key 后跑一次真实messages.create用最小max_tokens确保环境配置正确。八、排查清单报的是 401 还是 400401 多半是 key/头400 多半是 body/模型名。api_key末尾是否带了换行或空格读取后strip()一下。请求头是否同时有x-api-key和anthropic-versionConsole 默认如此。model名是否正确新版用claude-3-5-sonnet-latest部署环境与本地是否用了同一个 key服务器可能没注入环境变量。是否混用了Authorization: Bearer与x-api-key统一用 SDK 最稳。endpoint 是否指向api.anthropic.com/v1/messages九、小结Console 能跑、代码报错几乎从不是 key 失效而是代码侧请求与 Console 生成的请求在鉴权头/key 形态上不一致key 带换行、x-api-key头缺失、anthropic-version漏设、或model名写错。最小修复是用官方 SDK自动补齐头并对 keystrip()结构化做法是抽成ClaudeApiConsoleMismatchPolicy统一清洗与头构造最后用 pytest 守护key 必清洗、头必齐全、body 必含必填字段杜绝同类不一致。