博客

LLM API 返回 403:地区拦截、权限与策略拒绝

LLM API 的 403 是四个不同问题挤在同一个状态码下:地区策略、权限范围、策略拒绝,或你自己路径中的边缘拦截。每一类的修法都不同,而重试不属于任何一种。

LLM API 返回 403:地区拦截、权限与策略拒绝

在 LLM 集成中,403 是最容易被误读的状态码。当运维手册以 LLM request failed: 403 这一行开头时,人的直觉是把它当成打错号码的限流,或者一次重试就能清掉的认证小毛病。两者都不对。这个状态码覆盖四类完全不同的问题:地区策略拦截、凭据权限范围、策略与内容拒绝,以及在请求到达提供商之前、发生在你自己网络路径里的拦截。每一类的修法都不同,而对这四类来说,重试都是错误的第一反应。本文是针对单一状态码的深入解析,承接完整错误码排查;如果你是从 429 过来的,关于可重试限制的姊妹篇是速率限制指南。

LLM API 403 forbidden 不只是一个错误

在开始诊断之前,先把这四类的定义记牢,因为本文余下全部内容都是在区分它们。

  1. 地区与属地拦截。请求来自提供商不服务的地区,拒绝出于策略,而非故障。
  2. 凭据权限范围。密钥有效且被识别,但不被允许在这里做这件特定的事。
  3. 策略与内容拒绝。请求本身不被允许,与是谁在发起无关。
  4. 网关、代理与边缘拦截。你自己路径中的某个环节(企业出口管控、自建网关、安全产品)代替提供商作出了回应。请求从未到达。

注意缺了什么:这份清单上没有任何一项是暂时性状况。正是这一点决定了第一条规则。

规则一:401 问你是谁,403 问你被允许做什么

人们常把 401 和 403 当作可以互换的一对,而错误表比口口相传的讲法更清楚地划出了这条界线。OpenAI 的错误参考列出了与身份和组织相关的 401 情形:认证无效、密钥不正确、账号不属于任何组织,以及请求 IP 与已配置的允许列表不符。Anthropic 的错误表在其一侧为认证问题画出了同样的形状。而 403 这一族关乎的是授权:*** 权限范围,以及策略。这个区别重要,因为修法位于不同地方:身份问题在你的凭据配置里解决( Claude Code 凭据优先级那篇文章讲的就是那边多个凭据如何相互作用),而授权问题要在权限、策略或位置中解决。一个有用的简记法:401 意味着门不认识你;403 意味着门清楚知道你是谁,但仍然关着。

这就引出本文想要陈述的操作规则:不要把退避重试逻辑用在 403 上。速率限制指南里那套重试机制是为会自行变化的状况准备的;403 是一个决定,而决定不会随定时器到期。重试地区拦截或权限拒绝,只是花着额度重问一个答案早已存档的问题。

四类 403,逐一拆解

地区与属地策略

OpenAI 在其错误表里把这一条目标为 403 - Country, region, or territory not supported,原因写得很直白:你正从不被支持的国家、地区或属地访问该 API,解决方案则指向受支持国家页面。这一点值得读两遍:提供商把它当作带文档凭据的策略边界,而不是 bug。处理路径也由此而来。去提供商的页面上确认位置策略(对于你正在阅读本文的这个平台,对应的页面是我们自己的受支持地区页面),如果你的情况需要例外处理或正式答复,路径要经过提供商的官方渠道:客户代表或支持团队。这是一场关于合规与账号的对话,也是唯一一类把“改你的代码”当作答案完全跑偏的 403。

凭据权限范围

第二类看起来像认证问题,实则是授权问题:*** 没有问题,权限有问题。OpenAI 的文档在认证故障排查中处理了这一情形,把“API key 不具备该端点所需权限”列在原因之中,并将解决方向指向密钥、组织 ID 和项目级密钥。在 SDK 层,同样的情形表现为 PermissionDeniedError,其文档给出的原因是对所请求资源没有访问权限,给出的解决办法是检查 API key、组织 ID 和资源 ID。Anthropic 在错误表里直接点名了这一情形:403 permission_error,意为该 API key 无权使用指定资源,修复路径是控制台里你所在组织的访问设置与工作区设置。所以端点上的 API key permission denied 是一个配置问题,对应一份具体的清单:对的密钥、对的组织、对的工作区、对的资源 ID。如果这些都核对无误而拒绝依旧,下一步是找到能在提供商侧查看访问设置的管理员。

策略与内容拒绝

并非每个 403 都关乎访问权限;有些是平台拒绝了请求本身。这一类与其他 4xx 家族有交集,需要划清边界:400 类校验错误意味着请求格式有误,模型侧的拒答是模型在一个成功的响应内部表示拒绝,而策略拒绝是平台拒绝执行该操作。对这一类,实用的第一步始终相同:读错误响应正文。两个生态都会返回带有 type 和 message 的结构化错误,因此在你给状态码赋予含义之前,type 字段已经告诉你身处哪个家族。然后去读管辖你所尝试动作的策略文档,而不是反复改 prompt 碰运气。如果请求触发了策略边界,prompt 工程不是工具,读规则才是。

网关、代理与边缘拦截

第四类根本到不了提供商,这正是它的特征。自建网关、企业出口链路、CDN 或安全设备都可能用自己的 403 形式拒绝请求。诊断靠形状:API 错误响应是带 error 对象、type、message 和 request ID 的 JSON;边缘拦截通常返回别的东西——HTML 页面、一行纯文本、某种厂商特有的结构,而且没有 request ID,因为根本没有请求存在。Anthropic 的文档甚至从提供商侧展示了这个概念:它指出在其某一错误情形下,响应是在请求到达 API 服务器之前由边缘基础设施返回的,这正是要找的模式。修法属于网络考古,而非账号操作:比较一次直连调用与一次走完整路径的调用,把两份原始响应都留作证据,并与负责中间环节的人一起处理。

一条可以按序执行的判断路径

  1. 读响应正文,而不只是状态码。 先把其中error.type 和error.message 这两项写进事故记录。
  2. 检查有没有 request ID。如果正文和响应头里都没有,先怀疑第四类,而不是第一类。
  3. 归入四类之中。地区、权限、策略或边缘。
  4. 地区类:先查策略页面,其次走官方渠道。没有别的路径。
  5. 权限类:按顺序复核密钥、组织、工作区、资源 ID,然后转给管理员。
  6. 记录结论。属于哪一类、有哪些证据、改动了什么。这条记录应包含的字段清单(含 request ID)属于日志指南的范畴,也正是它让下一次发生从谜团变成查表。

支持工单里该附上什么

两家厂商都围绕同一件东西设计:request ID。Anthropic 的指引明确说每个响应都包含唯一的 request-id 头,同一个标识符也会出现在错误正文的 request_id 字段中,并且支持沟通中应当包含它。OpenAI 的错误参考则要求你在升级一个持续存在的错误时,提供你发送的请求数据与请求头。升级时请附上:request ID;UTC 时间戳;端点与模型;完整的错误类型与消息;这次调用是否经过任何网关;复现步骤;以及你已经排除了什么。这份清单刻意显得乏味,而它决定了工单是推进还是被退回。

把合规边界说清楚

对 AI 服务的地区限制,是厂商在真实法律约束下作出的策略与合规决定,与它们打交道的唯一持久方式,就是通过作出这些决定的同一批官方渠道:受支持地区文档,以及在你需要例外或澄清时的厂商支持或客户团队。本文刻意不讨论、不支持、也不暗示任何规避地区限制的方法,因为这些做法都不可靠,而且都会让账号、数据与商业关系承担风险。如果你的团队处在受限地区,有价值的对话是关于官方支持的路径:厂商在那里提供什么、必须运行的工作负载有什么替代方案、文档今天怎么说。这场对话,连同你的客户团队与法律顾问,也是唯一能产出你在审计中站得住脚的答案的那场对话。

本文的位置

本文属于一个诊断系列。完整的错误码排查勾勒了每个状态码家族;本文是其中之一的深入解析。重试指南说明何时重试才是对的,而本文的 403 规则正是刻意针对这个状态码与它相反的。日志指南提供了上文中用到的证据字段。而当你调用的端点位于某个兼容接口之后时,端点层面的事实就写在平台自己的文档。

403 检查清单

  1. 先分类:地区、权限、策略还是边缘。
  2. 确认来源:带 request ID 的 API 错误正文说明来自提供商;没有则指向路径。
  3. 地区类:只走策略页面与官方渠道。没有例外,也不耍聪明。
  4. 权限类:按顺序核对密钥、组织、工作区与资源 ID。
  5. 策略类:在动 prompt 之前,先读错误类型与管辖该动作的策略文档。
  6. 边缘类:比较直连与走完整路径的响应,并把两者都留作证据。
  7. 按证据清单升级,request ID 放在最前面。
  8. 永远不要对 403 做退避重试;并且在本地、流水线和生产环境中对同一个密钥建立基线,因为第四类恰恰藏在这些差异里。

四个问题,一个状态码,一条纪律:读正文、找分类、修对那一层。在真正要紧的时候,乏味的诊断永远胜过聪明的重试。