{"record":{"id":"04616a8e66c4c09e","repo":"BigPizzaV3/CodexPlusPlus","slug":"edits-content-type","errorCode":null,"errorMessage":"图片 edits 请求缺少 Content-Type","messagePattern":"图片 edits 请求缺少 Content-Type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/protocol_proxy.rs","lineNumber":1002,"sourceCode":"        crate::relay_config::relay_profile_base_url(&relay)\n    } else {\n        relay.upstream_base_url.trim().to_string()\n    };\n    if is_local_protocol_proxy_base_url(&base_url) {\n        anyhow::bail!(\"图片上游 Base URL 不能指向本地协议代理\");\n    }\n    if base_url.trim().is_empty() {\n        anyhow::bail!(\"图片上游 Base URL 不能为空\");\n    }\n    if relay.api_key.trim().is_empty() && !relay.uses_no_auth() {\n        anyhow::bail!(\"图片上游 Key 不能为空\");\n    }\n    let content_type = content_type.trim();\n    let content_type = if content_type.is_empty() {\n        match endpoint_kind {\n            ImageProxyEndpoint::Generations => \"application/json\",\n            ImageProxyEndpoint::Edits => {\n                anyhow::bail!(\"图片 edits 请求缺少 Content-Type\");\n            }\n        }\n    } else {\n        content_type\n    };\n    let endpoint = endpoint_kind.url(&base_url);\n    let wire_api = endpoint_kind.wire_api();\n    let _ = crate::diagnostic_log::append_diagnostic_log(\n        \"protocol_proxy.image_request\",\n        json!({\n            \"relayId\": relay.id,\n            \"relayName\": relay.name,\n            \"endpoint\": endpoint,\n            \"wireApi\": wire_api,\n            \"bodyBytes\": body.len(),\n            \"endpointKind\": endpoint_kind.name()\n        }),\n    );","sourceCodeStart":984,"sourceCodeEnd":1020,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/protocol_proxy.rs#L984-L1020","documentation":"For image edits requests, open_image_proxy_request requires a Content-Type header because edits use multipart or specific media types that cannot be defaulted. Generations requests default to application/json, but an edits request with an empty/missing Content-Type bails. The throw happens before the upstream request is built.","triggerScenarios":"Calling open_image_edits_proxy_request (via open_image_proxy_request) with content_type empty or whitespace after trimming.","commonSituations":"Client stripped the Content-Type header on the inbound edits request; a gateway/intermediary removed it; code constructing the edits proxy call forgot to pass the inbound content type.","solutions":["Ensure the client sends a Content-Type header (e.g. multipart/form-data with boundary) on image edits requests","If calling the internal API directly, pass the original request's content type to open_image_edits_proxy_request","Use the generations endpoint instead when submitting pure JSON requests"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if endpoint_is_edits && content_type.trim().is_empty() { return Err(\"edits request requires Content-Type\"); }","typeGuard":null,"tryCatchPattern":"match open_image_edits_proxy_request(...) {\n    Err(e) if e.to_string().contains(\"缺少 Content-Type\") => eprintln!(\"请为 edits 请求设置 Content-Type（如 multipart/form-data）: {e}\"),\n    other => other?,\n}","preventionTips":["Always forward the inbound Content-Type header to the proxy call","Keep multipart boundaries intact through any intermediary","Use the generations endpoint for pure JSON requests"],"tags":["http","validation","content-type","image-edits"],"backgroundTag":"missing-required-argument","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}