- API 请求错误 — 同步 API 调用的 HTTP 响应(认证、计费、请求格式、资源不存在等)。见 API 请求错误。
- Prediction 失败代码 — 请求已被接受并进入队列,但 prediction 本身以
status: "failed"结束。失败信息由 prediction 上的error对象描述,可从 prediction status 端点取得,webhook 与 result 端点也会送达。见 Prediction 失败代码。
Prediction 失败代码
失败的 prediction 携带一个error 对象:
失败的 prediction 不会计费,为该请求预留的额度会被释放。
prediction 若没有失败,其
error 为 null。若你在 2026 年 8 月 17 日之前完成对接,请看迁移说明。
错误会在哪里出现
同一个error 对象会在每条通道上发布,你在哪里查就在哪里读:
从 result 端点读取失败信息
对一个已失败的 prediction 请求它的输出,会返回400,并带上导致失败的那个错误,请求级 code 为 PREDICTION_FAILED:
error.code是PREDICTION_FAILED,属于 API 请求错误,含义是”你要取输出的那个 prediction 没有产出输出”;error.details才是 prediction 自己的error对象——与/status对同一请求报告的内容完全一致,因此两个端点不可能对失败原因给出不同说法。请按error.details.reason与error.details.retryable分支。
error.message 会复述 prediction 的 message,因此只读顶层 message 的客户端也能拿到有用信息。
两个 timestamp,两种含义。 顶层 timestamp 是这个 HTTP 响应产生的时刻;error.details.timestamp 是 prediction 失败的时刻。如果你几天后才来取,两者会相差几天。凡是想表达”失败发生在何时”,请读里层那个。
官方客户端库已经替你拆好:result() 抛出的错误,其 code、reason、retryable 就是 prediction 自己的值,与 subscribe() 对同一次失败抛出的完全一致。
仍在排队、仍在运行、或已被取消的 prediction,返回的仍是一直以来的那个通用 400。
如何决定要不要重试
看retryable。它只回答一个问题——把这份一模一样的请求再发一次,结果会不会不同——并且与 code 正交:同一个 code 在这次失败里可重试,在另一次里可能不可重试。
retryable 是可选字段。缺席时,回落到下表中按 code 的默认值。这正是该字段出现之前 Sunra 的行为,所以回落永远是安全的,只是不够精确。
code
reason
reason 指出 code 背后的具体原因。请把它用于指标统计、分流与技术支持定位;重试决策请用 retryable。
目前公布的每个 reason 在下方都有独立一节,锚点就是 reason 本身——#input_fetch_failed、#moderation_blocked 等——因此错误报告可以直接链到它的含义。
input_validation_failed
code: invalid_input · retryable: false
请求没有通过 Sunra 自己的 schema 或参数校验。message 会点名出问题的字段。
怎么办: 修正被点名的字段。原样重发只会再次失败。
provider_rejected_input
code: invalid_input · retryable: false
模型 provider 拒绝了一个或多个输入参数。provider 说明了是哪个时,message 会转述;有些 provider 只回复”输入不可接受”而不说明具体项,这种情况我们就如实这么说,不做猜测。
怎么办: 对照该模型的 schema 检查参数——取值越界、组合不受支持、宽高比或时长模型不接受等。
input_fetch_failed
code: invalid_input · retryable: false 或 true
以 URL 形式提供的输入文件拉取失败。这是唯一一个 retryable 会真正变化的 reason,所以请读字段,不要臆断:
false—— 对方主机拒绝了我们:4xx、不允许访问的地址、重定向,或文件体积超限;true—— 对方主机一时不可达:5xx、超时,或 DNS 解析失败。
message 会回显你提交的 URL,便于判断是哪个输入出了问题——但会去掉 query string,因此 presigned URL 中携带的凭证绝不会被回显给你:
retryable 为 false 时,检查该 URL 是否公网可达、presigned 链接是否已过期;为 true 时直接重试。
moderation_blocked
code: unsafe_content · retryable: false
被模型 provider 的内容安全系统拦截——可能拦的是送进去的 prompt 与素材,也可能是生成回来的结果。
怎么办: 修改 prompt 或素材。相同内容重发必然再次被拦。
provider_rate_limited
code: service_provider_error · retryable: true
模型 provider 正在限流。
怎么办: 带退避重试,或改走其他模型。
provider_unavailable
code: service_provider_error · retryable: true
模型 provider 未能完成本次请求。
怎么办: 稍后重试,或改走其他模型。
provider_timeout
code: task_timeout · retryable: true
prediction 未在允许时间内完成。
怎么办: 重试——低负载时段、或把请求做小(更短时长、更低分辨率)都能提高成功率。
provider_account_error
code: internal_server_error · retryable: false
我方在该模型 provider 处的账号有问题——欠费、凭证失效,或永久性配额。这是我们的问题,不是你输入的问题。
怎么办: 你这边无需处理;重试无济于事,我们已自动收到告警。若现在就需要这部分产能,请改走其他模型。
endpoint_misconfigured
code: internal_server_error · retryable: false
该模型 endpoint 在我方配置有误。
怎么办: 你这边无需处理;重试无济于事,我们已自动收到告警。若紧急,请携带 request_id 联系支持。
queue_enqueue_failed
code: internal_server_error · retryable: true
prediction 未能进入队列。
怎么办: 重新提交该请求。
reaped_stalled
code: internal_server_error · retryable: true
prediction 卡死后被终止。
怎么办: 重新提交该请求。
未知值
code 与 reason 都是开放集合。 随着我们对更多失败完成归类,新值会直接加入,不会另行发布 breaking change 公告。你的集成在遇到不认识的值时,不得崩溃、不得丢弃该错误、也不得拒绝整个 payload。
- 未知的
reason—— 当作reason缺席处理,改用retryable(或按code的默认值)决策。请把原始值记进日志:这是技术支持定位问题最快的线索。 - 未知的
code—— 当作internal_server_error处理。有一个哨兵值值得记住名字:unknown_error_code,表示 prediction 确实失败了,但 Sunra 无法为它恢复出任何分类。
迁移说明:没有错误时 error 为 null
2026 年 8 月 17 日起生效。 error 对象在每条通道上只有一种形态,通道之间原有的三处差异已经消失:
- prediction 没有失败时,
error为null。GET /v1/predictions/{prediction_id}过去对每个 succeeded、queued、cancelled 的 prediction 都返回一个空对象"error": {},现在返回"error": null——queue status 端点本来就是这么做的。如果你在判断”是不是空对象”(Object.keys(response.error).length === 0),请改成判空(response.error === null)。 - 失败一定带非空的
code与message。 若某次失败记下的内容无法归类,你拿到的是哨兵值unknown_error_code和一句通用 message,而不是缺席、空串或非字符串的字段。 - 以纯文本记录的失败,会以文本形式送达。 2026 年 4 月有少量 prediction 把 error 存成了一个裸字符串。
GET /v1/predictions/{prediction_id}过去会把它丢掉,queue status 过去会用哨兵 message 顶替它;现在两者都会把它作为message发布,并带上code: "unknown_error_code"。
error 本来就在上述每个响应里,变的只是它的值。Webhook 投递未受影响——其他通道正是收敛到了它的形态;succeeded 投递中 error 仍然是缺席,而不是发一个 null。
兼容窗口。 不存在新旧并发的过渡期:该日期起 API 不再产出 "error": {}。如果你要回放或重新处理此前抓取的 payload,在这批存量数据淘汰之前,请把 null 与 {} 同等对待。
即将发生的变化:部分失败会拿到更精确的 code
今天有相当大比例的失败被报为 service_provider_error,其中不少既不是暂态的,也不是 provider 的错。随着我们逐个 provider 完成归类,这些失败会迁移到真正描述它们的 code:
- provider 确定性地拒绝了你的输入 →
invalid_input(原为service_provider_error); - Sunra 侧的问题,例如 endpoint 配置错误或我方 provider 账号问题 →
internal_server_error(原为service_provider_error); - 输入文件拉取不到 →
invalid_input(原为internal_server_error)。
code 值——上表五个值仍然是已归类 code 的全集。变的是某次失败会落到其中哪一个,而且是按 provider 逐批、渐进发生的。如果你的代码在按 code 分支,现在正是把这段逻辑迁到 retryable 与 reason 上的好时机:它们直接描述失败本身,不受这次重新归类的影响。