Skip to content

fix(gateway): 修复跨账号 compaction 重写引起的499错误 与 zstd 大请求引发的413错误 - #397

Open
MDX-Tom wants to merge 2 commits into
qxcnm:mainfrom
MDX-Tom:codex/fix-zstd-compaction-recovery
Open

fix(gateway): 修复跨账号 compaction 重写引起的499错误 与 zstd 大请求引发的413错误#397
MDX-Tom wants to merge 2 commits into
qxcnm:mainfrom
MDX-Tom:codex/fix-zstd-compaction-recovery

Conversation

@MDX-Tom

@MDX-Tom MDX-Tom commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

背景

在启用官方 zstd 请求压缩,并使用 OpenAI provider name 后,长会话/图片场景可能出现两类失败:

  1. 跨账号候选切换或无状态重试时,上游返回 input[N].encrypted_content 缺失;
  2. 图片历史较大的 Responses 请求在前置代理解压后被固定 64 MiB 上限拦截,返回 413。

根因

1. compaction 被留下无效对象壳

跨账号恢复会移除账号绑定的 encrypted_content。旧逻辑会删除 compaction.encrypted_content 字段,但只把 reasoning/encrypted_content 类型的数组项整体移除,导致 compaction 本体继续发往上游。Responses API 仍要求该类型携带 encrypted_content,因此报 missing_required_parameter

2. zstd 路径存在未配置的 64 MiB 隐式上限

项目配置中 CODEXMANAGER_FRONT_PROXY_MAX_BODY_BYTES=0 表示不限制,普通请求也遵循该语义;但 zstd 解压路径额外把 0 映射成固定 64 MiB。官方 Codex 会将当前任务的完整 Responses JSON 编码后再用 zstd level 3 压缩,图片密集型长任务的解压尺寸可以超过 64 MiB,官方请求代码没有对应的 64 MiB 客户端上限。

修改

  • 跨账号/无状态恢复时,将 reasoningencrypted_contentcompactioncompaction_summarycontext_compaction 作为账号绑定历史项整体移除,避免留下缺字段对象壳。
  • zstd 解压严格遵循现有 FRONT_PROXY_MAX_BODY_BYTES 语义:
    • 0:不添加请求体大小拒绝;
    • >0:继续按解压后大小限制,并保留 413。
  • 保留 zstd 解压并发信号量(最多 4 个解压任务)和 spawn_blocking,未改变请求路由、缓存或图片加载逻辑。
  • 未修改本地或仓库 .cargo/config.toml

高概率复现

499

  1. 构造 /v1/responses 输入:前 24 项为普通 message,第 25 项为带 encrypted_contenttype=compaction,末尾再追加一条普通 message。
  2. 让首选账号失败/冷却,触发第二账号候选,使 strip_session_affinity=true
  3. 旧版会把 compaction 变成无 encrypted_content 的壳并发送,稳定触发 input[24].encrypted_content 缺失;修复后 compaction 项被整体移除。

413

  1. 构造包含文本和 data:image/png;base64,... 的合法 Responses JSON,使解压后长度为 67,108,865 bytes。
  2. 使用 zstd level 3 并设置 Content-Encoding: zstd,保持默认 body limit 语义(0)。
  3. 旧版稳定返回 413 ... after zstd decompression: >67108864;修复后成功解压并进入后端转发。
  4. 另用 max_body_bytes=32 和 64-byte 解压内容验证显式限制仍返回 413。

测试

通过:

  • cargo test -p codexmanager-service strip_encrypted_content -- --nocapture(2/2)
  • cargo test -p codexmanager-service stripped_candidate_removes_account_scoped_items_without_leaving_invalid_shells -- --nocapture(1/1)
  • cargo test -p codexmanager-service default_front_proxy_limit_decodes_official_image_request_above_64_mib -- --nocapture(1/1)
  • cargo test -p codexmanager-service decompressed_zstd_request_body_respects_front_proxy_limit -- --nocapture(1/1)
  • cargo test -p codexmanager-service zstd -- --nocapture(6/6)
  • gateway logs integration(39/39)
  • cargo test --workspace --exclude codexmanager-service(通过)
  • cargo fmt --all -- --check
  • git diff --check HEAD~2..HEAD

完整 cargo test --workspace 还会命中一个基线已存在的 macOS 临时目录规范化失败:directory_import_detects_same_size_file_replacement_and_fifo_entries;该用例在未应用本 PR 的基线 commit 上同样稳定失败。本 PR 涉及的回归、gateway 集成和其余 workspace 均已验证。

Commit 划分

  1. b619e0fc fix(gateway): drop account-scoped compaction on failover
  2. dcb2c6ca fix(gateway): honor unbounded zstd request body setting

Fixes #224

@MDX-Tom
MDX-Tom force-pushed the codex/fix-zstd-compaction-recovery branch from dcb2c6c to ee62ccf Compare July 28, 2026 05:41
@MDX-Tom MDX-Tom changed the title fix(gateway): 修复跨账号 compaction 重写与 zstd 大请求 413 fix(gateway): 修复跨账号 compaction 重写引起的499错误 与 zstd 大请求引发的413错误 Jul 28, 2026

@qxcnm qxcnm left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compaction 修复方向没有发现阻塞问题,但 zstd 解压改动存在可被压缩炸弹触发的内存放大风险,暂时不能合并。

默认 CODEXMANAGER_FRONT_PROXY_MAX_BODY_BYTES=0 时,当前实现会对 zstd 请求直接 read_to_end,使解压后大小完全不受限。该解压发生在后端鉴权之前;即便限制并发任务数量,高压缩比请求仍可能持续放大内存并导致 OOM。

可以取消旧的 64 MiB 限制以兼容官方大请求,但请保留独立、可配置且有安全默认值的解压后大小上限(或可靠的扩张比限制),不要让 0 等价于无限解压,并增加压缩炸弹/超高扩张比回归测试。compaction 部分可以保留,必要时也可以拆成单独 PR。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 更新最新版本后可以是用image模型,但是运行过程中会报413 Payload Too Large: request body too large:

2 participants