Skip to main content
错误会出现在两个不同的位置,使用两套不同的词汇:
  • API 请求错误 — 同步 API 调用的 HTTP 响应(认证、计费、请求格式、资源不存在等)。见 API 请求错误
  • Prediction 失败代码 — 请求已被接受并进入队列,但 prediction 本身以 status: "failed" 结束。失败信息由 prediction 上的 error 对象描述,可从 prediction status 端点取得,webhook 与 result 端点也会送达。见 Prediction 失败代码

Prediction 失败代码

失败的 prediction 携带一个 error 对象:
失败的 prediction 不会计费,为该请求预留的额度会被释放。 prediction 若没有失败,其 errornull。若你在 2026 年 8 月 17 日之前完成对接,请看迁移说明

错误会在哪里出现

同一个 error 对象会在每条通道上发布,你在哪里查就在哪里读:

从 result 端点读取失败信息

对一个已失败的 prediction 请求它的输出,会返回 400,并带上导致失败的那个错误,请求级 code 为 PREDICTION_FAILED
两层各有一个 code,分属两套词汇,不可混用:
  • error.codePREDICTION_FAILED,属于 API 请求错误,含义是”你要取输出的那个 prediction 没有产出输出”;
  • error.details 才是 prediction 自己的 error 对象——与 /status 对同一请求报告的内容完全一致,因此两个端点不可能对失败原因给出不同说法。请按 error.details.reasonerror.details.retryable 分支。
error.message 会复述 prediction 的 message,因此只读顶层 message 的客户端也能拿到有用信息。 两个 timestamp,两种含义。 顶层 timestamp 是这个 HTTP 响应产生的时刻;error.details.timestamp 是 prediction 失败的时刻。如果你几天后才来取,两者会相差几天。凡是想表达”失败发生在何时”,请读里层那个。 官方客户端库已经替你拆好:result() 抛出的错误,其 codereasonretryable 就是 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 中携带的凭证绝不会被回显给你:
怎么办: retryablefalse 时,检查该 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 卡死后被终止。 怎么办: 重新提交该请求。

未知值

codereason 都是开放集合。 随着我们对更多失败完成归类,新值会直接加入,不会另行发布 breaking change 公告。你的集成在遇到不认识的值时,不得崩溃、不得丢弃该错误、也不得拒绝整个 payload。
  • 未知的 reason —— 当作 reason 缺席处理,改用 retryable(或按 code 的默认值)决策。请把原始值记进日志:这是技术支持定位问题最快的线索。
  • 未知的 code —— 当作 internal_server_error 处理。有一个哨兵值值得记住名字:unknown_error_code,表示 prediction 确实失败了,但 Sunra 无法为它恢复出任何分类。

迁移说明:没有错误时 errornull

2026 年 8 月 17 日起生效。 error 对象在每条通道上只有一种形态,通道之间原有的三处差异已经消失:
  • prediction 没有失败时,errornull GET /v1/predictions/{prediction_id} 过去对每个 succeeded、queued、cancelled 的 prediction 都返回一个空对象 "error": {},现在返回 "error": null——queue status 端点本来就是这么做的。如果你在判断”是不是空对象”(Object.keys(response.error).length === 0),请改成判空(response.error === null)。
  • 失败一定带非空的 codemessage 若某次失败记下的内容无法归类,你拿到的是哨兵值 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 值——上表五个值仍然是已归类 code 的全集。变的是某次失败会落到其中哪一个,而且是按 provider 逐批、渐进发生的。如果你的代码在按 code 分支,现在正是把这段逻辑迁到 retryablereason 上的好时机:它们直接描述失败本身,不受这次重新归类的影响。

API 请求错误