PyO3/pyo3 · error
invalid hex encoding
Error message
invalid hex encoding
What it means
Assertion inside the hex-decoding helper unescape() used to round-trip mangled symbol/identifier names in the build config. The input string was produced by escaping every byte as two hex characters, so its length must be even; an odd-length (or otherwise corrupt) escaped string fails this length invariant before any byte is decoded.
Source
Thrown at pyo3-build-config/src/impl_.rs:2700
escaped.push(LUT[(byte >> 4) as usize] as char);
escaped.push(LUT[(byte & 0x0F) as usize] as char);
}
escaped
}
fn unescape(escaped: &str) -> Vec<u8> {
assert_eq!(escaped.len() % 2, 0, "invalid hex encoding");
let mut bytes = Vec::with_capacity(escaped.len() / 2);
for chunk in escaped.as_bytes().chunks_exact(2) {
fn unhex(hex: u8) -> u8 {
match hex {
b'a'..=b'f' => hex - b'a' + 10,
b'0'..=b'9' => hex - b'0',
_ => panic!("invalid hex encoding"),
}
}
bytes.push((unhex(chunk[0]) << 4) | unhex(chunk[1]));
}
bytes
}
#[cfg(test)]
// can remove this expect when fields are private
#[expect(deprecated, reason = "accessing config fields directly for testing")]
mod tests {
use target_lexicon::triple;
use super::*;
#[test]View on GitHub (pinned to ac9b6899d3)
Solutions
- Fix the input value so it only contains lowercase hex pairs [0-9a-f]
- Clear and re-set the offending env/config variables (e.g. via cargo clean and a clean environment)
- Upgrade or align pyo3 crate versions so encoder and decoder match
Defensive patterns
Strategy: validation
Validate before calling
fn is_valid_hex_pairs(s: &str) -> bool {
s.len() % 2 == 0 && s.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
} Prevention
- Only pass values produced by pyo3's own escaping code into build-script env vars
- Clean the build environment (cargo clean) when upgrading pyo3
- Align all pyo3-* crate versions in the dependency tree
When it happens
Trigger: An environment/config value routed through pyo3_build_script_impl contains a non-hexadecimal character where two hex digits are expected during unescaping.
Common situations: Corrupted or hand-edited config/env values passed into the build script; library version mismatch where the producer encoded differently than this decoder expects.
Related errors
- `pyo3_build_config::get()` requires a direct dependency on `
- Unknown Py_GIL_DISABLED value
- Converting PyErr arguments failed: {}
- attempted to fetch exception but none was set
- failed to import exception {}.{}: {}
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/7876fa85ae467d55.
Report an issue: GitHub.