{"record":{"id":"ffb64dcaf5734437","repo":"BigPizzaV3/CodexPlusPlus","slug":"content-encoding-encoding","errorCode":null,"errorMessage":"不支持的 Content-Encoding：{encoding}","messagePattern":"不支持的 Content-Encoding：(.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/launcher.rs","lineNumber":1303,"sourceCode":"\nfn decode_protocol_proxy_request_body(\n    body: &[u8],\n    content_encoding: Option<&str>,\n) -> anyhow::Result<String> {\n    let encoding = content_encoding.unwrap_or_default().trim();\n    let decoded = if encoding.is_empty() || encoding.eq_ignore_ascii_case(\"identity\") {\n        body.to_vec()\n    } else if encoding.eq_ignore_ascii_case(\"zstd\") {\n        let decoder = zstd::stream::read::Decoder::new(std::io::Cursor::new(body))?;\n        let mut limited = decoder.take((MAX_HTTP_BODY_BYTES + 1) as u64);\n        let mut decoded = Vec::new();\n        limited.read_to_end(&mut decoded)?;\n        if decoded.len() > MAX_HTTP_BODY_BYTES {\n            anyhow::bail!(\"解压后的请求体超过大小限制\");\n        }\n        decoded\n    } else {\n        anyhow::bail!(\"不支持的 Content-Encoding：{encoding}\");\n    };\n\n    String::from_utf8(decoded)\n        .map_err(|error| anyhow::anyhow!(\"Responses 请求体不是 UTF-8：{error}\"))\n}\n\nfn overlay_image_response() -> (String, Vec<u8>, String, &'static str) {\n    let not_found = || {\n        (\n            \"404 Not Found\".to_string(),\n            serde_json::to_vec(&serde_json::json!({\n                \"status\": \"failed\",\n                \"message\": \"图片覆盖层未启用或图片不可用\"\n            }))\n            .unwrap_or_default(),\n            \"application/json; charset=utf-8\".to_string(),\n            \"helper.overlay_image_not_found\",\n        )","sourceCodeStart":1285,"sourceCodeEnd":1321,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/launcher.rs#L1285-L1321","documentation":"decode_protocol_proxy_request_body (crates/codex-plus-core/src/launcher.rs:1302-1303) accepts exactly three Content-Encoding values for Responses bodies forwarded through the local proxy: absent/empty, `identity`, and `zstd`. Anything else — gzip, br, deflate, x-gzip, or a typo — fails before decoding with this message. The proxy pipeline only implements zstd decoding, so other encodings are explicitly unsupported rather than silently mis-parsed.","triggerScenarios":"POSTing to the local helper/proxy Responses endpoint with `Content-Encoding: gzip` (or br/deflate) on the body; an HTTP client library with automatic transparent compression enabled (e.g. reqwest gzip feature sending gzip automatically); a middleware chain that re-encodes the body between client and proxy.","commonSituations":"Switching the client stack to one that defaults to gzip compression; adding a compressing reverse proxy in front of the loopback helper; version change where the client began negotiating br with a CDN and forwards the encoded body verbatim; hand-rolled fetch code setting Content-Encoding manually.","solutions":["Make the client send the body uncompressed: disable automatic compression for requests to the local proxy (reqwest: build the client without gzip/brotli features or .no_gzip().no_brotli(); fetch/axios: skip compression headers)","Re-encode as zstd if you must compress: Content-Encoding: zstd is the only compressed form the proxy decodes","If you control the core, extend the matcher at launcher.rs:1291-1304 with a gzip branch (flate2::read::GzDecoder) plus tests mirroring the zstd ones"],"exampleFix":"// before: gzip-compressed body trips the guard\nlet mut res = surf::post(url)\n    .header(\"content-encoding\", \"gzip\")\n    .body(gzip_compress(body))\n    .await?; // bail: 不支持的 Content-Encoding：gzip\n\n// after: identity body passes\nlet mut res = surf::post(url)\n    .header(\"content-encoding\", \"identity\")\n    .body(body)\n    .await?;","handlingStrategy":"validation","validationCode":"// Validate the header before sending\nfn proxy_supported_encoding(enc: Option<&str>) -> bool {\n    matches!(enc.map(str::trim), None | Some(\"\") | Some(\"identity\") | Some(\"zstd\") | Some(\"ZSTD\"))\n}\nassert!(proxy_supported_encoding(request_headers().get(\"content-encoding\")));","typeGuard":"fn is_supported_content_encoding(value: &str) -> bool {\n    let v = value.trim();\n    v.is_empty() || v.eq_ignore_ascii_case(\"identity\") || v.eq_ignore_ascii_case(\"zstd\")\n}","tryCatchPattern":"// Server side: map to 415 Unsupported Media Type\nlet body = match decode_protocol_proxy_request_body(&raw, content_encoding) {\n    Err(e) if e.to_string().starts_with(\"不支持的 Content-Encoding\") => {\n        return http_415_unsupported_media_type();\n    }\n    r => r?,\n};","preventionTips":["Build HTTP clients with compression disabled for local proxy targets (reqwest: .no_gzip().no_brotli().no_deflate())","Standardize on zstd (the only codec the proxy decodes) when compression is required","Never place a compressing reverse proxy in front of the loopback helper"],"tags":["rust","http-proxy","content-encoding","gzip","zstd","unsupported-encoding"],"backgroundTag":"unsupported-content-encoding","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}