阶跃星辰错误码对照:HTTP 异常与 finish_reason 怎么排
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
排阶跃星辰的报错,第一件事是分清它属于哪一层。官方文档把异常明确拆成两类:HTTP 层面的异常和模型层面的异常。前者是请求压根没跑完,看状态码;后者是请求成功返回了 200,但内容不完整或被拦,要看 finish_reason。最容易踩的坑有两个:一是 429 在阶跃这边有两种完全不同的来源——速率限制和组织 Credit 额度上限——官方文档专门提醒这两种情况状态码相同,收到 429 时要以错误标识来区分,处置方式一个是退避重试、一个是找主账号调额度;二是「模型不存在或无权限」在官方错误码表里归在 400 而不是 404,按 404 的思路去查请求路径会白查一圈。
先把两层异常分开:状态码 vs finish_reason
阶跃星辰的《异常事件处理建议》开门见山说了,它提供的异常类型一共两种:HTTP 层面的异常,和模型层面的异常。这个划分不是文档编排上的凑数,它直接决定你的代码要写在哪儿。
HTTP 层面的异常,在 SDK 里通常表现为抛出异常、请求失败,你的重试逻辑、告警逻辑挂在这一层。模型层面的异常则相反:HTTP 请求是成功的,响应体也拿到了,但里面的 finish_reason 告诉你这次生成没按预期结束。很多线上事故的形态是「接口一直没报错,但用户看到的回答莫名其妙被截断」,根因就在这一层,而监控只盯了状态码,什么都没抓到。
所以一个健康的接入层,这两处都要有分支处理,不能只写 try/except。
401、402、404:请求根本没进到模型
这三个错误码的共同点是问题出在请求本身或账户状态上,模型完全没参与。
401 认证无效。 官方给的解决方向是「确保使用正确的 API 密钥」。听起来像废话,但在组织场景下它有几个具体的诱因值得先排:其一,阶跃的项目 API Key 文档写明,已创建的 Key 删除后立即失效且无法撤回,如果有人在控制台清理过密钥,本地环境变量里那把就废了,需要在该项目下重新创建;其二,Key 的状态如果显示为「已被管理员禁用」,说明是主账号做的操作,这时候怎么改代码都没用,只能联系主账号处理。文档也提到,如果确认密钥无误但问题仍在,可以联系官方技术支持。密钥本身的管理规范可以参考API Key 安全管理这篇通用篇。
402 余额不足。 个人账号这边的处置很直接,官方说明是尽快到开放平台充值以恢复正常调用。但组织场景下 402 有一个专属的错误标识 insufficient_credit,含义是组织的 Credit 账户余额不足,官方给的恢复方式不是自己去充,而是「由主账号联系销售补充 Credit」。这两条路径不能混,成员账号自己在页面上找充值入口是找不到的。
404 请求路径不正确。 官方建议是对照文档检查并修复请求路径信息,具体到两步:先确认路径格式是否符合 API 规范,包括大小写、特殊字符是否准确;如果修完还是 404,再去看文档里关于路径结构和层级的说明。实践中这个错最常见的触发点是 base_url 配错——阶跃的 OpenAI 兼容迁移文档里,Python SDK 那一段明确写的是把 base_url 设为 https://api.stepfun.com/v1,少写或多写路径段都会落到 404 上。
429 有两种来源,别只会退避重试
这是阶跃错误码体系里最需要单独拿出来讲的一个。
常见错误码表里的 429,原因写的是「请求的资源超限,可能原因是您发送请求太快,超过了速率限」,解决方案是稍候重试。这是速率限制那一路。而在组织额度相关的错误码表里,429 又出现了两次:
project_credit_limit_exceeded:项目达到当期 Credit 上限,解决方式是由主账号调高项目上限,或等待下月 1 号重置;member_project_credit_limit_exceeded:成员在该项目内达到当期 Credit 上限,解决方式是由主账号调高该成员的上限,或等待下月 1 号重置。
官方文档在这里有一句很关键的提醒:其中两种情况的错误码与速率限制相同,请以错误标识区分。也就是说,光看状态码 429 是无法判断该怎么办的——如果是速率限制,退避重试有意义;如果是额度上限,你退避一万次也不会自己好,因为额度要到自然月周期重置或由主账号调整才会恢复。这意味着你的重试封装必须读错误标识,而不是见 429 就无脑指数退避。通用的 429 处理骨架可以看429 限流的通用处理方式,阶跃这边只是在骨架上多了一道分支。
速率限制这一路本身,阶跃在《基本介绍》里列了三种衡量方式:RPM(每分钟请求数)、TPM(每分钟 Token 数)、并发数(同时在线请求数)。文档明确写道,速率限制可能会在以上任何一种选项中达到,具体取决于哪种限制先被触发——注意这是任意一项触顶即触发,不是三项都超才算。这个逻辑关系写反了,你的自检脚本就会给出完全错误的结论。三个维度各自的含义在RPM 与 TPM 到底限的是什么里有更细的拆解。
至于怎么把限额提上去,官方的路径是分层的:《异常事件处理建议》说可以先设定一定的 delay 时间后再请求,如果 delay 仍无法解决,可以通过充值获得更高的频次;充值后的限额依然不够,则联系官方开通更高的频次。常见问题页也印证了这套口径,它说平台会根据账户累计充值金额实施相应的速率限制策略,具体数值要到文档中心的定价与限速页面查自己账号对应的档位。具体数值本文不复述,以官方页面当前版本为准。
另外有一条容易被忽略的边界:Credit 额度规则里写明,项目与成员的月度上限只约束 Credit 用量,不影响速率限制。这两套限制是各管各的,调高了额度上限不等于并发也跟着放开了。
额度类 429 的判定口径:三者取最小
如果确认是额度类的 429,还得知道它是怎么算出来的,否则很难跟主账号说清要调哪一项。
官方给的公式是,一次调用实际可扣减的 Credit 不超过三者中的最小值:Credit 账户余额、项目当期剩余上限、成员当期剩余上限。本次调用的用量超过该值时,调用被拒绝,且不产生扣费。「不产生扣费」这半句值得记一下——被额度挡住的请求不会白扣钱,所以对账时看到调用失败但计费明细里没有对应记录,是符合预期的,不是漏记。
还有几条周期规则会影响你的判断:上限的统计周期是自然月,每月 1 号 00:00 重置;月中启用上限时,当月按完整上限计算,不按剩余天数折算;月中修改上限值时,已用量不清零、周期也不重置,新的上限值直接减去已用量。最后这条很反直觉——主账号把上限调高之后,如果本月已用量本来就逼近新上限,成员可能过一会儿又被挡住,看起来像是「调了没生效」。
文档还提到,项目上限之和可以超过 Credit 账户余额,成员上限之和也可以超过所在项目的上限,实际可用额度取其中最紧的一项。所以后台里配的数字加起来好看,不代表真的能用出去。
400 和 451:请求内容本身有问题
400 的原因清单在官方错误码表里是明列出来的,一共五条:图片无法下载、图片数量超过限制、该模型不支持视频输入、模型不存在或无权限、参数值不合法。这份清单的信息量很大,因为它把三类完全不同的问题塞进了同一个状态码:多模态素材的问题(前三条)、模型权限的问题(第四条)、参数格式的问题(第五条)。
尤其要留意「模型不存在或无权限」被归在 400。按直觉这更像 404,但阶跃这边不是。所以看到 400 时,除了核对参数,还应该顺手确认一下 model 字段拼写是否正确、当前账号对这个模型有没有权限,别一头扎进参数表里逐个比对。
《异常事件处理建议》对 400 补了一条工程上的做法:如果请求信息中包含 UGC 内容,可以在应用程序里前置做输入验证,参考 API 的限制提前拦,让用户更早感知到自己输入的问题,而不是等接口打回来再解释。
451 是内容审核未通过,官方描述是请求内容或者响应内容未审核通过,解决方案是修改请求信息后再重试。注意这里的措辞包含了「响应内容」——也就是说,你的输入完全干净,模型生成的内容触线一样会拿到 451。文档给的建议同样是前置添加安全审核能力,先在自己这一侧完成验证。对于面向 C 端的产品,451 的文案设计需要提前想好,直接把原始报错抛给用户体验会很差。
500 / 503 / 504:服务端的问题
错误码表里 500 写的是「我们服务器上的问题」,建议稍等片刻后重试,问题仍在则联系官方;503 写的是「目前服务器负载过高」,建议稍候重试。《异常事件处理建议》把 500、503、504 归成一类统一表述:这类是阶跃星辰服务端出现的问题,遇到 5XX 可以稍等重试,多次重试后依然无法解决则联系官方定位。
工程上这一类是唯一适合配置自动重试的:401 重试没意义,402 和额度类 429 重试没意义,451 重试大概率还是 451,只有 5XX 和速率限制类 429 值得退避后再试。把重试策略按错误码分开配,比全局一刀切要省得多,也不会在额度耗尽时把无效请求刷成一片。
请求成功了,但结果不对:看 finish_reason
模型层面的异常靠 finish_reason 判断。官方《异常事件处理建议》列了四种取值和各自的处理方向:
stop:模型按预定计划结束生成,可以直接处理 Message;length:模型没有按预定计划结束,受限于 max_token 未能完整输出;content_filter:模型虽然按预定计划结束生成,但未通过安全审核;tool_calls:模型希望调用函数,需要做相应处理,并把返回的函数调用结果传入下一次请求。
这里有个细节值得注意:Chat Completions 的接口参考页在描述 finish_reason 字段时,写的可选值是 stop(正常结束)、length(达到 max_tokens 上限)与 tool_calls(模型发起工具调用),而 content_filter 的说明出现在异常处理指南里。两处文档的枚举范围不完全一致,写分支处理时建议按更宽的那一份来兜底,并对未知取值留一条默认分支,具体以官方文档当前版本为准。
length 是最需要业务侧介入的一种。它不是错误,接口返回一切正常,只是输出被 max_tokens 截住了。如果你的下游要解析 JSON,截断的输出会直接导致解析失败,而报错栈看起来完全是解析器的问题,跟模型无关,排查方向很容易带偏。稳妥的做法是在解析之前先判断 finish_reason 是不是 length,是的话直接走重试或续写逻辑,不要让残缺的字符串进解析器。
流式请求下要逐 chunk 判断
流式场景下这件事更隐蔽。官方明确说明,使用流式请求时,请求可能会在生成过程中因为模型输出原因结束,因此需要在获取到服务器的流式返回时,根据每一个 chunk 返回的 finish_reason 进行下一步处理。官方示例代码的写法是遍历 completion 的每个 chunk、再遍历其中的 choices,分别对 stop 和 content_filter 做判断。
也就是说,流式下不存在「等全部收完再统一看结束原因」这条捷径——结束原因是随流一起过来的,你不在循环里接,就等于没接。
自己排不动的时候,该给官方什么
阶跃单独有一页《故障排查指南》,讲的就是联系官方时要附上什么信息,好让对方能定位到具体那一次调用。按接口类型分了三种:
- Chat API(以及文生图、图生图、图片编辑、音频生成、复刻音色、音频转写):提供返回响应头 Header 中的
X-Trace-Id字段,以及生成结果中的id; - Realtime API:提供
session.created和session.updated中的session.id字段; - 流式生成音频 API:提供任一事件中的
session_id字段。
这份清单反过来是一条接入期就该做的事:把 X-Trace-Id 响应头和响应体里的 id 一起写进日志。等真出问题再去补埋点,那次出问题的调用早就没痕迹了。响应头是很多 SDK 默认不暴露给业务代码的,需要显式取,接入时顺手做掉成本很低。
收尾:一份可以照着写的分支表
把上面的东西收成接入层的判断顺序,大致是这样:先看有没有 HTTP 异常;有的话取状态码,401 查密钥与禁用状态,402 分个人余额和组织 Credit 两条路,404 查 base_url 与路径,400 先查 model 字段和权限再查参数与素材,451 走内容策略,5XX 退避重试;429 单独拎出来读错误标识,是速率限制就退避,是 project_credit_limit_exceeded 或 member_project_credit_limit_exceeded 就转人工找主账号。没有 HTTP 异常的,再读 finish_reason,流式下逐 chunk 读。
最容易栽的坑还是那两个:把 429 当成只有一种;以及把 200 响应默认当成成功。前者会让你的重试在额度耗尽时空转,后者会让截断和被拦的输出静悄悄流到用户面前。这两处各加一个分支,线上大部分莫名其妙的现象就有解释了。