openai/codex · error · io::Error

invalid network proxy attribution frame

Error message

invalid network proxy attribution frame

What it means

The first byte on the connection was NUL -- the first byte of the 8-byte magic b"\0CDXPXY1" -- so the ingress tried to read a full attribution frame, but the eight magic bytes did not match the constant. Rejected with io::ErrorKind::InvalidData. Meaning: the client speaks some other NUL-prefixed binary protocol, or wrote a corrupted, truncated, or wrong-version preface.

Source

Thrown at codex-rs/network-proxy/src/attribution.rs:88

        self.inner.serve(stream).await.map_err(Into::into)
    }
}

async fn read_attribution_token(stream: &mut TcpStream) -> Result<Option<String>, BoxError> {
    let mut marker = [0_u8; 1];
    let read = stream.stream.peek(&mut marker).await?;
    if read == 0 {
        return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "empty proxy connection").into());
    }
    if marker[0] != ATTRIBUTION_FRAME_MAGIC[0] {
        return Ok(None);
    }

    let token = tokio::time::timeout(ATTRIBUTION_FRAME_TIMEOUT, async {
        let mut magic = [0_u8; ATTRIBUTION_FRAME_MAGIC.len()];
        stream.read_exact(&mut magic).await?;
        if &magic != ATTRIBUTION_FRAME_MAGIC {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "invalid network proxy attribution frame",
            ));
        }

        let token_len = stream.read_u16().await? as usize;
        if token_len == 0 || token_len > MAX_ATTRIBUTION_TOKEN_LEN {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "invalid network proxy attribution token length",
            ));
        }
        let mut token = vec![0_u8; token_len];
        stream.read_exact(&mut token).await?;
        String::from_utf8(token).map_err(|_| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                "network proxy attribution token is not UTF-8",

View on GitHub (pinned to 339751715c)

Solutions

  1. Stop hand-rolling the preface: call write_attribution_frame(writer, &token), which writes the exact magic constant.
  2. Build the full frame (magic + length + token) in one buffer and write_all it once right after connect.
  3. If builds are version-skewed, rebuild bridge and ingress from the same source so ATTRIBUTION_FRAME_MAGIC matches.

Example fix

// before: hand-written magic with a typo
writer.write_all(b"\0CDXPXY2")?; // wrong constant
// after: use the shipped helper
codex_network_proxy::write_attribution_frame(&mut writer, &token)?;
Defensive patterns

Strategy: validation

Validate before calling

// Do not hand-roll bytes; route every preface through the helper
use codex_network_proxy::write_attribution_frame;
write_attribution_frame(&mut writer, &token)?; // writes the exact magic

Try / catch

Match io::ErrorKind::InvalidData whose message contains 'invalid network proxy attribution frame': stop and inspect the client's preface bytes (wrong magic constant or version skew); retrying the same bytes will fail identically.

Prevention

When it happens

Trigger: A hand-rolled client writes a different magic (typo, stale constant, different protocol version); the preface write is split and the stream shifts; another binary protocol that happens to start with 0x00 is pointed at the ingress port.

Common situations: Reimplementing the frame format in another language instead of reusing write_attribution_frame; a magic-constant change between bridge and ingress builds (version skew); routing a non-codex NUL-first protocol to the proxy port.

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/9b3f5e2ff7d745dc. Report an issue: GitHub.