雨天小六

读懂 Codex(5.22):连接错误、Provider 错误和模型可恢复错误

· 更新于 2026-08-02 · 专栏:读懂 Codex

#Codex#Agent Runtime#Responses API#流式协议#软件架构

网络断开、HTTP 429、服务端 failed event 和 Provider 特有认证错误起初形状完全不同。Codex 先在 codex-api 保留传输细节,再由 Provider 映射成统一 CodexErr;上层只依据语义类别决定重试、回退、提示或终止。

具体问题与启用条件

本节建立错误分层与可恢复性矩阵。ContextWindowExceeded 单独在 5.23,401 token 刷新作为认证特例在此说明。

条件来源决定字段或状态对本机制的影响
传输HTTP/WS 建连、发送、读取产生 Transport/Stream ApiError
Responses 事件failed/incomplete产生模型/服务端 ApiError
Provider默认 OpenAI-compatible 或 Bedrock override映射用户可见语义

协议、类型与状态所有权

状态或协议所有者生命周期关键不变量
TransportError/ApiErrorcodex-client/codex-api单次尝试保留状态、Header、body、URL
Provider map_api_errorModelProvider错误边界允许后端特有解释
CodexErrCore请求/Turn 控制流携带 retryable 与 retry_delay
Error/StreamError EventMsgSession客户端事件不进入模型 History

机制调用链如下:

socket/HTTP/Responses failed
→ ApiError(Transport|Stream|Context|Quota|Retryable|Invalid...)
→ ModelProvider.map_api_error
→ CodexErr semantic category
→ 401:一次 auth recovery/rebuild request
→ retryable:stream retry budget
→ non-retryable:Error event + Turn 结束
→ WS budget exhausted:HTTP fallback
传输、Responses 和 Provider 错误的归一化层次
图 5.22-1:上层只在 Provider 映射后依据 CodexErr 的语义做恢复。

机制怎样工作

SSE failed event按 code 直接区分 context、quota、usage-not-included、cyber policy、invalid prompt/bio policy、server overloaded 与普通 Retryable。HTTP/WS wrapped error还保留 status、headers 和 body。默认 bridge 把 400 映射 InvalidRequest(并识别无效图片/安全策略),500 映射 InternalServerError,结构化 429 usage limit 映射不可重试 UsageLimitReached,其余状态保留为 UnexpectedStatus;Bedrock 等 Provider 可以覆盖映射,例如把过期签名转成明确用户提示。

CodexErr.is_retryable() 是上层权威:Stream、timeout、部分 unexpected status、connection/internal 错误可重试;invalid request、quota、usage limit、cyber policy、server overloaded、context error等不走普通流重试。这里 server overloaded 虽可能稍后恢复,当前采样重试策略仍将其视为非重试,避免在已知容量信号上自动打满。401 是连接建立/请求前的专门恢复路径:若有 AuthManager,最多刷新并重建认证尝试;永久刷新失败变成 RefreshTokenFailed。

Python 风格伪代码

这段伪代码保留生产实现中会改变结果的状态、分支和异步边界;认证 SDK、遥测字段和 Rust 所有权样板被折叠为明确的领域对象。

async def request_with_auth_recovery(make_request, auth: AuthManager | None):
    recovery_used = False
    while True:
        try:
            return await make_request(await current_auth_headers())
        except HttpUnauthorized as error:
            if auth is None or recovery_used:
                raise provider.map_error(error)
            recovery_used = True
            await auth.refresh_or_raise()

def classify_api_error(error: ApiError, provider: ModelProvider) -> CodexError:
    mapped = provider.map_error(error)
    return CodexError(
        category=mapped.category,
        retryable=mapped.category in RETRYABLE_CATEGORIES,
        retry_delay=mapped.retry_delay,
        diagnostics=mapped.safe_diagnostics,
    )

async def handle_sampling_error(error: CodexError):
    if error.retryable:
        await retry_or_fallback(error)
    else:
        await emit_turn_error(error)
        raise error

失败、取消与恢复

不同错误类别的恢复动作矩阵
图 5.22-2:认证刷新、流重试、传输回退和用户可处理错误保持独立。
故障或边界已发生的状态对上层的结果能否直接重试恢复动作
Network/early stream close请求结果未知或不完整Stream/Connection error退避重试
401 且刷新成功原请求未完成透明重建一次尝试一次使用新认证
400 invalid request服务端拒绝语义InvalidRequest修改输入/模型能力
429 usage limit额度状态可解析UsageLimitReached更新 Session 限流并提示
Provider 特有错误底层细节保留Provider 定制 CodexErr依映射例如刷新 AWS 凭据

设计取舍与验证

分两次映射让通用协议层不依赖具体 Provider,同时保留后端定制错误消息。代价是同一错误要穿过多个枚举,新增类别需同步测试 is_retryable 和客户端协议映射。把可重试性集中到 CodexErr 避免各调用点凭字符串判断。

可验证契约证据方式预期结果
failed code 映射到具体 ApiErrorSSE parser 参数化测试context/quota/policy 等不混为 Stream
503 overloaded body 特判api bridge 单元测试得到 ServerOverloaded
Provider 可覆写错误消息Bedrock error tests签名过期有专用提示

Mini Codex 对照

Mini Codex 应至少有 TransportError、ProtocolError、ModelRequestError 三层,并让 Provider Adapter 提供 map_error。不要用 except Exception: retry;可重试集合必须穷举测试。

本节边界

错误分类已经到达 Turn 控制流。5.23 单独分析 ContextWindowExceeded,因为它不是普通重试,而会影响下一次压缩决策。

评论


← 返回文章列表