{"record":{"id":"d5db8887f1621c79","repo":"openai/codex","slug":"network-proxy-attribution-token-is-not-utf-8","errorCode":null,"errorMessage":"network proxy attribution token is not UTF-8","messagePattern":"network proxy attribution token is not UTF-8","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/network-proxy/src/attribution.rs","lineNumber":104,"sourceCode":"        stream.read_exact(&mut magic).await?;\n        if &magic != ATTRIBUTION_FRAME_MAGIC {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"invalid network proxy attribution frame\",\n            ));\n        }\n\n        let token_len = stream.read_u16().await? as usize;\n        if token_len == 0 || token_len > MAX_ATTRIBUTION_TOKEN_LEN {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"invalid network proxy attribution token length\",\n            ));\n        }\n        let mut token = vec![0_u8; token_len];\n        stream.read_exact(&mut token).await?;\n        String::from_utf8(token).map_err(|_| {\n            io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"network proxy attribution token is not UTF-8\",\n            )\n        })\n    })\n    .await\n    .map_err(|_| {\n        io::Error::new(\n            io::ErrorKind::TimedOut,\n            \"network proxy attribution frame timed out\",\n        )\n    })??;\n\n    Ok(Some(token))\n}\n\n/// Writes the trusted bridge preface consumed by the shared proxy ingress.\n#[doc(hidden)]","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/network-proxy/src/attribution.rs#L86-L122","documentation":"The declared number of token bytes was read successfully but is not valid UTF-8; the ingress requires the attribution token to be a UTF-8 string and rejects with InvalidData. The shipped writer takes &str, so this only happens with hand-built frames: a binary token (raw hash/UUID bytes) or a length prefix that is off, causing framing bytes to be swallowed into the token field.","triggerScenarios":"A custom writer puts raw binary (16-byte UUID, SHA digest) into the token field; token_len is off by the header size so read_exact consumes part of the next field; deliberate fuzz/malformed input after a correct magic.","commonSituations":"Porting the preface to another language and passing bytes instead of str; length arithmetic that accidentally includes the magic or length fields in token_len.","solutions":["Pass a UTF-8 string token -- hex- or base64-encode binary ids first.","Recompute token_len as exactly token.as_bytes().len() with no header bytes included.","Use write_attribution_frame, whose &str parameter rules non-UTF-8 tokens out by construction."],"exampleFix":"// before: raw binary id used as the token\nlet token: &[u8] = uuid.as_bytes(); // may be non-UTF-8\n// after: textual encoding\nlet token = uuid.to_string(); // ASCII hyphenated form","handlingStrategy":"validation","validationCode":"// Reject before writing the frame when building bytes yourself\nif std::str::from_utf8(token_bytes).is_err() {\n    return Err(io::Error::new(\n        io::ErrorKind::InvalidInput,\n        \"attribution token must be valid UTF-8\",\n    ));\n}","typeGuard":"fn is_utf8_token(bytes: &[u8]) -> bool {\n    std::str::from_utf8(bytes).is_ok()\n}","tryCatchPattern":"Match io::ErrorKind::InvalidData whose message contains 'not UTF-8': re-encode the token as hex/base64 and recompute token_len; if the token was textual, the length prefix is off -- audit the framing arithmetic.","preventionTips":["Keep tokens textual (hex or base64 for binary ids).","Set token_len = token.as_bytes().len() exactly, with no header bytes.","Prefer the &str-taking write_attribution_frame helper, which cannot emit non-UTF-8 tokens."],"tags":["rust","codex","network-proxy","utf-8","token","invalid-data"],"backgroundTag":"invalid-utf8","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}