pydantic/monty · info
one character
Error message
one character
What it means
Internal `expect('one character')` in `hex_separator`: it extracts the first char of a separator string already checked to have length exactly 1 (the `length != 1` guard raises `ValueError('sep must be length 1.')` beforehand), so `chars().next()` cannot be `None`. The user-facing failures here are the real `ValueError`s for wrong-length or non-ASCII separators, not this panic.
Source
Thrown at crates/monty/src/modules/binascii.rs:501
/// CPython measures the length before it looks at the type, so an unsized
/// separator reports "has no len()" and a sized one of the wrong length is a
/// `ValueError` — "sep must be str or bytes." only applies to a sized object
/// of length one. `None` is not "no separator": omitting `sep` leaves it
/// unset, so an explicit `None` fails the length check as any other object.
fn hex_separator(sep: Option<&Value>, vm: &VM<'_>) -> RunResult<Option<u8>> {
let Some(sep) = sep else {
return Ok(None);
};
let length = sep
.py_len(vm)
.ok_or_else(|| ExcType::type_error(format!("object of type '{}' has no len()", sep.py_type_name(vm))))?;
if length != 1 {
Err(value_error("sep must be length 1."))
} else if sep.is_str(vm.heap) {
// Latin-1, not ASCII: `hexlify` returns bytes, so CPython only rejects
// a character that does not fit a byte, under an "ASCII" message.
u8::try_from(u32::from(sep.to_str(vm)?.chars().next().expect("one character")))
.map(Some)
.map_err(|_| value_error("sep must be ASCII."))
} else if is_bytes(sep, vm) {
Ok(Some(encode_input(sep, vm)?[0]))
} else {
Err(ExcType::type_error("sep must be str or bytes."))
}
}
/// Whether a value is `bytes`, which is all `hexlify` accepts as a separator
/// besides `str` — a buffer that `encode_input` would take is still rejected.
fn is_bytes(value: &Value, vm: &VM<'_>) -> bool {
match value {
Value::InternBytes(_) => true,
Value::Ref(heap_id) => matches!(vm.heap.get(*heap_id), HeapData::Bytes(_)),
_ => false,
}
}View on GitHub (pinned to adc986b362)
Solutions
- Pass a single-character ASCII (Latin-1 encodable) string or one-byte bytes as `sep`.
- Check `len(sep) == 1` before calling if the separator is dynamic.
Example fix
// before binascii.hexlify(data, sep='ab') # ValueError: sep must be length 1. // after binascii.hexlify(data, sep=b'-')
Defensive patterns
Strategy: validation
Validate before calling
if not isinstance(sep, (str, bytes)) or len(sep) != 1:
raise ValueError('sep must be length 1.')
if isinstance(sep, str) and ord(sep) > 0xFF:
raise ValueError('sep must be ASCII.') Type guard
def is_single_byte_sep(sep) -> bool:
if isinstance(sep, bytes):
return len(sep) == 1
return isinstance(sep, str) and len(sep) == 1 and ord(sep) <= 0xFF Try / catch
try:
out = binascii.hexlify(data, sep=sep)
except ValueError as exc:
# 'sep must be length 1.' or 'sep must be ASCII.'
handle_bad_separator(exc) Prevention
- Validate separator length and encodability before calling hexlify.
- Prefer passing sep as bytes (b'-') to sidestep encoding questions.
- For dynamic separators derived from user input, clamp with a allowlist of single ASCII characters.
When it happens
Trigger: Unreachable panics; the reachable errors are `ValueError: sep must be length 1.` (multi-char or empty sep) and `ValueError: sep must be ASCII.` (character above U+00FF) from `binascii.hexlify`.
Common situations: Users passing `sep='ab'`, `sep=''`, or a non-Latin-1 character (e.g. 'é' works as Latin-1, '€' fails) to `hexlify`.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- u32 fits usize on supported targets
- gather commit frame id is not a GatherFuture
- gather item is not a Coroutine, ExternalFuture, or GatherFut
- Cannot get identity of Dereferenced object
- invalid ascii byte
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/bd6806ff800bc042.
Report an issue: GitHub.