GLM 鉴权失败逐条排查:从 API Key 到 JWT 签名
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
GLM 鉴权失败别只盯着 HTTP 401。智谱开放平台的响应码是两层结构,外层是 HTTP 状态码,内层是响应体里的业务错误码,真正能定位问题的是内层那个数字。官方文档在 401 底下列了四个不同的业务码:1001 是请求头里压根没收到鉴权参数,1000 是身份验证没通过,1003 是 Token 已过期需要重新生成,1005 是账号开了二次认证保护。这四个的处置动作完全不同。而更容易浪费时间的情况是:报错根本不在鉴权层——Key 用错了产品线、Base URL 配成了另一条协议的地址、账户欠费,这几种在官方错误码表里被归到 429 或 403,症状却很像「密钥不好使」。
下面按我认为最省时间的顺序排一遍。
第一步:把响应体完整打印出来,别只记状态码
官方错误码文档写得很明确:调用智谱开放平台 API 时收到的响应码由两部分组成,外层是 HTTP 状态码,内层是响应体正文里定义的业务错误码,后者提供更具体的错误描述。
文档给的响应报文样例长这样——HTTP 状态是 401,响应体是一个 error 对象,里面有 code 和 message 两个字段,code 的值是字符串形式的业务码,message 是中文描述。也就是说,你的客户端如果只捕获了状态码就抛异常,等于把最有用的那一半信息扔了。
这里还有一个容易踩的例外,官方在错误码页面底部专门加了注:使用流式(SSE)调用时,如果 API 在推理过程中异常终止,不会返回上述错误码,而是在响应体的 finish_reason 参数里返回异常原因。所以流式场景下「没有错误码」不代表没出错,得去读 finish_reason。跨厂商的通用排查思路可以对照 API 报 401 / 403 怎么排查 那篇,这里只讲 GLM 自己的特殊之处。
1001:请求头里根本没带鉴权参数
官方对 1001 的描述是「Header 中未收到 Authentication 参数,无法进行身份验证」。这是最纯粹的一种失败——服务端没看到你的凭证。
GLM 请求头的写法只有一种形式:开放平台 API 使用标准的 HTTP Bearer 进行身份验证,API 密钥通过 HTTP 请求头提供,格式是 Authorization: Bearer YOUR_API_KEY。另外还要带上 Content-Type: application/json。(后面会讲到官方还提供了 JWT 鉴权,但那条路生成出来的凭证同样是放进这个 Bearer 头里传的,头的格式没有第二种。)
实际会导致 1001 的写法,多半是这几类:
- 把 Key 直接塞进了请求体或 query 参数,请求头是空的
- 写了请求头但漏掉
Bearer前缀,只放了裸 Key - 从环境变量读 Key,但变量没生效,最后拼出来的是
Bearer加一个空串 - 某些 HTTP 客户端库会自动剥掉它不认识的 header,代理层也可能把
Authorization过滤掉
排查动作很直接:先用 curl 按官方示例的形式打一次,官方文档里的 curl 示例就是 --header 'Authorization: Bearer YOUR_API_KEY' 加 --header 'Content-Type: application/json'。curl 能通、代码不能通,问题就在你的客户端封装那一层,跟平台无关。
1000:Key 本身没通过验证
1000 的官方描述是「身份验证失败」,比 1001 往后走了一步——头带上了,但这个凭证不被接受。
先确认 Key 的来源。官方给的路径是:登录后在 API Keys 管理页面创建 API Key,然后复制使用。申请流程细节可以看 GLM API Key 怎么申请。
然后是最反直觉的一条:GLM 的 Key 不是一把通吃的。官方在编程套餐快速开始里明确写了,团队版套餐的成员从团队编程套餐页面获取 API Key,并且加了一句「团队套餐 Key 与平台其他 API Key 不通用,使用团队额度请务必使用团队套餐 Key」。个人版套餐的用户则是从个人编程套餐的套餐概览里新建 Key。
产品线错配的报错也不一定长成 401。官方错误码表里有一条 1315,HTTP 状态是 429,文案是「该 API Key 仅限企业编程套餐场景使用,请到官网更换对应产品类型的 API Key」。一个 429 的码在说 Key 用错了产品线,这个设计确实有点绕,但知道了就能少绕一圈。
还有一条属于产品边界而非技术故障:官方明确 GLM Coding Plan 仅限在官方支持的指定工具与产品环境中使用,在规定工具之外调用 API 不能享用套餐额度;如果要在自建应用、网站、机器人、SaaS 产品这类场景里通过 API 集成模型能力,官方要求使用标准 API 服务并按对应协议计费。也就是说,拿套餐 Key 去跑自建服务,即使技术上通了,也不在套餐的授权范围内。
端点配错,症状很像鉴权问题
通用 API 端点官方写的是 https://open.bigmodel.cn/api/paas/v4。但文档同时挂了一个 Warning:使用 GLM 编码套餐时,需要配置专属的 Coding 端点。
编程套餐支持两种协议接入,官方在快速开始里列了三个 Base URL,按协议类型区分:Anthropic Message 协议、OpenAI Chat Completion 协议、OpenAI Response 协议,各自对应不同的路径。这是一个结构性枚举,以官方文档当前版本为准。
配错的后果是:请求打到了一个不认这把 Key 的入口上,返回什么码取决于那一侧的判定逻辑,很多时候看起来就是「密钥无效」。所以排查鉴权时,请务必把 Base URL 和 Key 的来源放在一起核对——这两个必须是配套的。Claude Code 侧的完整配置步骤可以看 在 Claude Code 里用 GLM 编程套餐。
顺带一提,官方在 Anthropic 兼容的接入文档里建议把 Key 设置为环境变量 ANTHROPIC_API_KEY,替代硬编码到代码中。用环境变量的好处不只是安全,还包括排查时能一眼看出到底注入了哪一把 Key。
1003:Token 过期,以及 JWT 签名这条路
1003 的官方描述是「Authentication Token 已过期,请重新生成/获取」。官方给的处置动作就写在这句错误信息里:重新生成,或重新获取。至于这个码具体在哪一种鉴权方式下才会出现,官方文档里没有找到相关说明——错误码表只给了这一句,并没有把 1003 与某一条鉴权路径绑定,所以别看到 1003 就默认自己一定走的是某种路径。
不过这句描述里的措辞值得留意:它说的是 Token,不是 Key。而在官方列出的两种鉴权方式中,只有 JWT 这条路需要你自己构造一个带过期时间字段的凭证,也就是说这条路上多出了一整套「自己算过期时间」的代码,出岔子的面积比直接贴 Key 大得多。所以这一节顺带把 JWT 的构造过程拆开讲。
官方在 HTTP API 调用文档里列了两种鉴权方式:一种是 API Key 鉴权,最简单,直接把 Key 放进 Bearer 头;另一种是 JWT Token 鉴权,官方的说法是「适合需要更高安全性的场景」,需要先装 PyJWT。
官方给的 Python 示例做了这么几件事,每一步都对应一处自己实现时容易写偏的地方:
- 把 apikey 按
.切成两段,前一段是 id,后一段是 secret。切不出两段就直接抛invalid apikey——所以 Key 里的那个点不是分隔符装饰,它是格式的一部分,复制时被截断或被编辑器加了空白字符,第一步就挂 - 构造 payload,包含三个字段:
api_key放 id,exp是过期时间,timestamp是当前时间戳 - 用 secret 做签名,算法是 HS256
- 自定义 JWT 头,带
alg和sign_type两个字段,sign_type的值是SIGN
第 4 步是最容易被漏掉的:官方示例是在 jwt.encode 里显式传了 headers={"alg": "HS256", "sign_type": "SIGN"},而 sign_type 是一个自定义头字段,通用 JWT 库默认生成的头里不会自己冒出来。官方文档没有说明缺了它服务端会怎么响应,所以这里只给一条操作建议:照抄官方示例的 headers 参数,别自己简化。
第 2 步要盯的是时间量级。官方示例里 exp 和 timestamp 都是从 int(round(time.time() * 1000)) 算出来的,也就是毫秒;exp 的写法是这个毫秒数再加上 exp_seconds * 1000,示例调用处传的 exp_seconds 注释写着 1 小时有效期。换句话说,两个字段的量级在官方示例里是统一的毫秒,你自己实现时若沿用别处的秒级习惯,签出来的凭证声明的到期时刻就跟官方示例完全不是一个数量级。官方文档同样没有说明这种情况服务端会返回哪个码,判断方法是把自己生成的 payload 打印出来,跟官方示例的量级对一眼。另外本机时钟如果漂移得厉害,timestamp 也会跟着不可信,这一步属于环境检查而不是代码检查。
再补一条:实时接口(realtime)的鉴权说明里写着 Authorization 支持 JWT(客户端)或 Bearer API Key(服务端),格式仍然是 Bearer 开头。也就是说 JWT 这条路的设计意图之一,是让前端拿到一个短期凭证,而不必把长期 Key 下发到客户端。这个取舍值得单独想清楚,密钥分发与轮换的通用做法见 API Key 安全管理。
1005:账号开了二次认证保护
官方对 1005 的描述是「已开启二次认证保护,需要二次认证登录」,HTTP 状态同样是 401。
这一条的特殊之处在于:官方原文把处置动作写成了「需要二次认证登录」,落点在账号侧而不是代码侧。也就是说,请求头怎么改、Key 怎么换都不会让它消失。至于二次认证的开关具体在控制台哪个页面配置、由谁来解除,官方文档里没有找到相关说明,别照着别人给的路径瞎找,直接从登录后的账号设置里翻或者问平台客服更快。
从排查效率上说,这个码的价值在于它能立刻把方向从代码切走:如果一批环境是同时开始报 1005 的,那么变量就不在任何一份代码里,而在这个账号的登录保护配置上。
这些看着像鉴权,其实不是
官方错误码表里有几条特别容易被误判成鉴权失败:
- 1220 / HTTP 403:「您无权访问某个 API」。身份是认出来了,是权限不够。这属于开通与授权的问题,不是密钥的问题
- 1113 / HTTP 429:「您的账户已欠费,请充值后重试」。欠费落在 429 家族里,而不是 401 家族。看到 429 容易先入为主当成限流,实际上得读内层码才知道是账户状态问题
- 1302 / HTTP 429:账户已达到速率限制。这是限流,跟身份无关
- 1309 / HTTP 429:GLM Coding Plan 套餐已到期。套餐过期表现为限流码而不是鉴权码
- 1311 / HTTP 429:当前订阅套餐暂未开放某个模型的权限。这条尤其像「Key 没权限」,但根因是套餐档位与模型的对应关系
- 1211 / HTTP 400:模型不存在,请检查模型代码。写错模型名会走 400,不会走 401
判断口诀可以简化成:401 家族看凭证,403 看授权,429 家族看额度与套餐,400 看参数。 只要先把内层业务码读出来,落到哪一族是一目了然的。
实名认证与鉴权是两件事
中文里「认证」这个词把两件事混在了一起,实际排查时常有人绕进去。
官方 FAQ 里写得很清楚:目前调用 API 并不强制要求实名认证,但为确保账户安全,官方建议进行实名认证。所以「我没实名,是不是因此鉴权失败」这个猜测,方向就是错的。
不过实名类型确实会影响一些账号侧能力,官方列了这几条:企业认证账号和个人认证账号的认证方式与提交材料不同;完成企业认证的开发者可以享有企业权益;完成企业认证的开发者可以通过对公打款方式充值。官方还特别提醒,强烈建议企业账号不要使用个人身份做个人实名认证,以免企业人员变动或交接引发账号登录信息丢失或产生不必要的纠纷——这条建议看着像客套话,但它指向的是一个真实风险:Key 挂在离职员工的个人身份下。
方向性的限制也值得记住:个人实名账号可以变更为企业实名认证,官方在实名认证页面提供了「变更为企业认证」的入口;反过来,企业实名账号不支持更改为个人账号。海外企业官方也支持认证,需要在实名认证页面选择海外企业并上传相关材料。认证数量方面,官方的说法是每个企业、每个人可以实名认证多个账号,目前没有认证数量限制。
审核时长按认证类型不同而不同,官方分别给了个人人脸识别、企业法人人脸识别、企业营业执照授权、企业对公打款四类的时效说明,具体以官方实名认证页面当前的说明为准。
排查顺序与几条卫生习惯
把上面的内容压成一条动线:
- 先读内层业务码和
message,别只看 HTTP 状态 - 用 curl 按官方示例打一发,区分是平台侧还是客户端封装侧的问题
- 核对 Base URL 与 Key 是否配套——通用端点和编程套餐的专属端点不是一回事
- 确认 Key 的产品线:平台标准 API、个人套餐、团队套餐,各有各的 Key
- 走 JWT 的话,重点查三处:Key 能不能按点切成两段、时间字段的量级、JWT 头里有没有
sign_type - 落到 429 或 403 家族,就转去查额度、套餐和权限,不要继续在密钥上打转
最后是官方在 HTTP API 文档实践建议里给的三条密钥卫生:妥善保管 API Key、不要在代码中硬编码;使用环境变量或配置文件存储敏感信息;定期轮换 API Key。第三条最常被跳过,但它同时解决两个问题——泄露风险,以及「这个环境到底在用哪把 Key」这种查起来最费劲的问题。
真正最容易栽的坑其实只有一个:看到 401 就开始重新生成 Key。生成新 Key 是成本最低的动作,所以大家都先做它,结果是把一个端点配错或套餐产品线错配的问题,用一堆新 Key 掩盖了过去,最后攒了一柜子来路不明的密钥。先读业务码,再动手。