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

  1. Pass a single-character ASCII (Latin-1 encodable) string or one-byte bytes as `sep`.
  2. 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

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


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/bd6806ff800bc042. Report an issue: GitHub.