CModel 错误模型
CModel Error Contract 1.0.0 的三字段响应结构、HTTP 语义和调用规则。
CModel Error Contract 1.0.0 用于百积木拥有的非流式 HTTP 和 Module 调用边界。公共响应只包含 contractVersion、errorCode 和 data 三个字段;服务不能再输出 value、systemCurrentTime、error 或 { success, message }、{ code, message } 等私有响应。
公共机器可读源事实与 SDK:
- CModel 响应 JSON Schema
- npm:
npm install @baijimu/cmodel
每个公开 API 必须在自身文档中登记调用方能够观察和处理的稳定 errorCode。平台内部可以保留错误目录、分类、诊断信息和类型,但这些内部字段不能序列化进公共响应,也不能改变 CModel 固定的 HTTP 200。
适用边界
本契约适用于百积木拥有的非流式 HTTP API 和 Module 方法响应。
以下边界保留各自的协议 envelope,并由最外层 Adapter 与 CModel 做一次确定性转换:
- 健康检查和就绪探针;
- SSE、WebSocket 等已经发出响应头的流式协议;
- MCP、OpenAI、Anthropic 和第三方供应商协议;
- 浏览器重定向、文件下载等非 JSON 响应。
外部错误格式不能继续进入百积木内部业务链路。
成功响应
{
"contractVersion": "1.0.0",
"errorCode": "0",
"data": {
"id": "example"
}
}成功响应必须同时满足:
- HTTP 状态为
200; contractVersion缺省时按1.0.0解析;显式提供时必须为严格 SemVer1.0.0;errorCode为字符串"0";data由具体 API 或 Runtime 方法的返回类型定义。
使用 CModel envelope 的接口不能返回 204 No Content。
失败响应
{
"contractVersion": "1.0.0",
"errorCode": "PAYMENT_REQUIRED",
"data": {
"message": "当前账户余额不足,请充值后重试",
"retryable": false
}
}失败响应必须同时满足:
- HTTP 状态为
200; errorCode是非"0"的稳定字符串错误码;data可以为null,或包含必填message与retryable的标准公开错误详情对象;message是 1 到 512 个字符、可直接展示给用户的安全文案;retryable表示在不改变输入、授权或账户状态时,原操作能否通过重试成功。
业务失败必须使用 HTTP 200。HTTP 只表示 CModel envelope 已被正常传输,业务成败只由 errorCode 表达:"0" 为成功,非 "0" 为失败。网关、代理或容器在 CModel Handler 之外产生的非 200 响应属于传输或基础设施失败,不得伪装成 CModel 业务响应。
Envelope 与业务数据边界
CModel envelope 解析器只读取并校验三个已知字段:
contractVersionerrorCodedata
解析器必须忽略其他顶层字段。旧响应中残留的 value、systemCurrentTime、error 及其子字段都按未知顶层字段处理,不能导致协议解析失败,也不能继续作为业务判断或用户展示来源。为兼容协议版本字段引入前的 1.0.0 响应,消费者在 contractVersion 缺失时必须补成 1.0.0;一旦字段存在,非法 SemVer 或非 1.0.0 版本仍是协议错误。新生产者只能输出三个标准字段,并显式输出 contractVersion。
成功响应 data 的内部结构不由 CModel envelope 校验,具体 API、Module 方法或 Runtime methodDefinition 负责定义和校验。失败响应 data 由 CModel 统一定义为 null 或标准公开错误详情;消费者必须校验 message 和 retryable,并原样保留对象中的未知属性,以允许具体 API 在 1.0.0 中追加已文档化的公开操作信息。未知属性不能用于错误分类;分类仍只依赖该 API 登记的稳定 errorCode。
服务级错误传播路径
失败详情可携带可选的 data.errorPath,表示当前错误返回时经过的逻辑服务,按外层服务到错误源排列。
它是非空字符串数组,每个标识非空且不含空白或控制字符;允许真实的重复服务调用。
只有相应 API 已登记可公开的逻辑服务标识才能对外输出,内部服务拓扑不因该字段自动成为公共契约。
{
"contractVersion": "1.0.0",
"errorCode": "PAYMENT_REQUIRED",
"data": {
"message": "当前账户余额不足,请充值后重试",
"retryable": false,
"errorPath": ["checkout", "payment"]
}
}示例标识仅用于解释排列顺序,不是平台服务目录。
平台 Runtime 和模块 API 登记以下公开诊断标识;它们表示逻辑职责,不包含部署地址或实例身份:
| 逻辑服务标识 | 职责 |
|---|---|
runtime | Runtime 请求执行入口 |
runtime-assembly | Runtime 装配与 Bundle 操作 |
module | 模块定义与版本 |
例如 errorPath: ["runtime", "runtime-assembly", "module"] 表示错误由模块服务返回,经过 Runtime 装配服务和执行入口传出。路径仅包含这次错误实际经过且已接入的服务,不保证覆盖尚未接入的服务;部署副本数不增加路径层级。服务身份由版本化配置登记,公开标识的新增或变化须同步 API 文档。
错误源初始化路径,每个上游服务由统一 SDK/响应出口
在数组头部追加自己的配置身份,一次服务调用只追加一次。客户端解析只校验并保留路径,不追加当前服务,
不修改原始 errorCode、message、retryable 或其他业务详情,也不把路径拼进 message。
服务内的方法栈、源码行号、实例地址和语言级 stacktrace 不写入路径;完整诊断通过追踪上下文关联内部日志。 该路径只描述返回的这次错误,不是完整调用图,不能用于鉴权、错误分类或重试决策。
该可选扩展保持 1.0.0 及原有必填字段:data: null 仍合法,不能改写成仅含 errorPath 的对象。
新接入的错误生产者应提供完整 message、retryable 及路径;上游不得为了补路径编造缺失的错误详情或
未收到的下游路径。成功响应的业务 data 不受此诊断字段约束,也不自动追加路径。
公网 Adapter 对未登记为公开诊断的内部路径执行移除,或按明确的 API 契约映射为公开逻辑服务标识。 映射不依赖消息文本,外部输入的路径不能冒充可信内部服务路径。
错误身份与公开边界
平台内部错误目录可以继续保存错误所有者、原因、分类、重试语义、用户文案和诊断索引,但公共调用方只依赖当前 API 文档登记的:
errorCode- 该 API 对此错误的稳定说明
data.message,仅用于用户展示data.retryable,仅用于决定是否自动重试
最外层 Adapter 负责把内部错误确定性映射成 errorCode,只把已经审查为可公开展示的用户文案和稳定重试语义写入失败 data,并把有效 CModel envelope 统一输出为 HTTP 200。原始上游错误消息、数据库错误、堆栈、Token、Secret、内部域名、完整请求体和个人数据不能进入公共响应。
HTTP 消费规则
调用方收到有效 CModel envelope 后,必须解析 envelope 并以 errorCode 判断业务结果:
- HTTP
200可以承载成功或业务失败;errorCode == "0"为成功,非"0"为失败。 - 失败响应的
data可以是null或标准{ message, retryable }对象;null兼容已有生产者。 contractVersion缺失时按1.0.0处理;显式值非法或不受支持时报告协议错误,不做版本协商。- 不接受数字
0、error_code、顶层code/message或缺失errorCode。 - 忽略所有未知顶层字段;成功
data交给具体接口的类型解析器,失败data交给 CModel 标准错误详情解析器。标准解析器校验message、retryable并保留端点扩展字段,但不得用扩展字段猜测错误类别。 - 错误分类只使用
errorCode和当前 API 文档;不得用 HTTP 状态、value、data.message或上游自然语言文本分类。
为支持已上线服务滚动迁移,SDK 1.1.4 的消费者可以解析历史非 200 失败 envelope,但仍只依据非零 errorCode 返回业务失败;所有新生产者和已迁移生产者必须输出 HTTP 200。当生产者盘点确认不再存在历史响应后,删除这项有明确退出条件的 consumer 兼容行为。
流式错误
流式协议不使用 CModel envelope。负责该流的协议必须定义自己的版本化错误事件,并在进入百积木非流式业务边界时由所属 Adapter 完成一次确定性转换。
不得把流内失败当作普通文本、成功数据或 JSON 解析异常吞掉。
收敛要求
生产者、消费者和 Gateway 必须直接收敛到同一个 1.0.0 三字段模型:
- 消费者只解析三个已知字段并忽略未知顶层字段;缺失的
contractVersion统一规范化为1.0.0。 - 生产者只序列化三个字段。
- Gateway 保留已经校验的错误码、原始消息和业务详情,仅按服务出口及公开可见性规则补充或移除
data.errorPath,对调用方统一输出 HTTP200。 - 不增加双版本或版本协商;滚动迁移兼容只存在于 SDK consumer 边界,并以生产者全量迁移为删除条件。
- 生产者/消费者契约测试必须同时覆盖 HTTP
200成功与失败、未知顶层字段、成功data类型所有权,以及失败data为null和标准错误详情两种形态。