BigPizzaV3/CodexPlusPlus · error

Audio transcriptions 请求缺少 Content-Type

Error message

Audio transcriptions 请求缺少 Content-Type

What it means

Thrown by open_audio_transcriptions_proxy_request in crates/codex-plus-core/src/protocol_proxy.rs when the incoming audio transcriptions request has an empty Content-Type header (after trimming). The proxy must forward the original multipart boundary to the upstream verbatim — it re-sends the raw body with .header(CONTENT_TYPE, content_type) — so without a Content-Type (normally multipart/form-data; boundary=...) the upstream cannot parse the multipart audio upload.

Source

Thrown at crates/codex-plus-core/src/protocol_proxy.rs:815

        status_code,
        is_stream: false,
        content_type,
        wire_api: UpstreamWireApi::Responses,
        response: upstream,
    })
}

pub async fn open_audio_transcriptions_proxy_request(
    body: &[u8],
    content_type: &str,
    original_user_agent: Option<&str>,
) -> anyhow::Result<UpstreamProxyResponse> {
    let settings = SettingsStore::default().load().unwrap_or_default();
    let relay = crate::relay_rotation::select_relay_for_probe(&settings)?;
    validate_upstream(&relay)?;
    let content_type = content_type.trim();
    if content_type.is_empty() {
        anyhow::bail!("Audio transcriptions 请求缺少 Content-Type");
    }

    let endpoint = audio_transcriptions_url(&relay.base_url);
    let _ = crate::diagnostic_log::append_diagnostic_log(
        "protocol_proxy.audio_transcriptions_request",
        json!({
            "relayId": relay.id,
            "relayName": relay.name,
            "endpoint": endpoint,
            "wireApi": UpstreamWireApi::AudioTranscriptions,
            "bodyBytes": body.len()
        }),
    );
    let upstream = send_upstream_request(
        crate::http_client::proxied_client(&effective_user_agent(
            &relay.user_agent,
            original_user_agent,
        ))?

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Set Content-Type on the client request to the multipart type including the exact boundary used to encode the body, e.g. multipart/form-data; boundary=X
  2. Prefer a proper multipart helper (reqwest::blocking::multipart, curl -F) so the boundary in the header matches the body
  3. Verify no intermediate proxy strips the header before the request reaches this endpoint

Example fix

# before
curl -X POST http://127.0.0.1:8137/v1/audio/transcriptions --data-binary @audio.mp3

# after
curl -X POST http://127.0.0.1:8137/v1/audio/transcriptions -F file=@audio.mp3 -F model=whisper-1
Defensive patterns

Strategy: validation

Validate before calling

let content_type = request
    .headers()
    .get(reqwest::header::CONTENT_TYPE)
    .and_then(|v| v.to_str().ok())
    .unwrap_or("")
    .trim()
    .to_string();
if content_type.is_empty() {
    return bad_request("multipart Content-Type with boundary is required");
}

Type guard

fn has_multipart_content_type(ct: &str) -> bool {
    ct.starts_with("multipart/form-data") && ct.contains("boundary=")
}

Prevention

When it happens

Trigger: Calling the /v1/audio/transcriptions proxy path with a client that omitted the Content-Type header; an HTTP client set to strip headers, or code building the multipart body manually and forgetting to set the header; a proxy/framework in front dropping the header.

Common situations: Custom scripts posting audio without multipart tooling; curl invocations missing -H 'Content-Type: multipart/form-data; boundary=...'; middleware that buffers the body and drops the original Content-Type; testing tools that default to no content type.


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/c15974f970628308. Report an issue: GitHub.