neondatabase/neon · error

failed to parse {one:?} as hex or span attributes: - {e1:#}

Error message

failed to parse {one:?} as hex or span attributes:
- {e1:#}
- {e2:#}

What it means

With a single positional argument, the key subcommand tries two parsers in order: Key::from_hex (the fixed-width hexadecimal form of a key) and SpanAttributesFromLogs::from_str (a pasted log line containing rel= and blkno=). If both fail, this combined error reports both parse causes so you can see why neither form matched.

Source

Thrown at pageserver/ctl/src/key.rs:196

impl<S: AsRef<str>> TryFrom<&[S]> for KeyMaterial {
    type Error = anyhow::Error;

    fn try_from(value: &[S]) -> Result<Self, Self::Error> {
        match value {
            [] => anyhow::bail!(
                "need 1..N positional arguments describing the key, try hex or a log line"
            ),
            [one] => {
                let one = one.as_ref();

                let key = Key::from_hex(one).map(KeyMaterial::Hex);

                let attrs = SpanAttributesFromLogs::from_str(one).map(KeyMaterial::String);

                match (key, attrs) {
                    (Ok(key), _) => Ok(key),
                    (_, Ok(s)) => Ok(s),
                    (Err(e1), Err(e2)) => anyhow::bail!(
                        "failed to parse {one:?} as hex or span attributes:\n- {e1:#}\n- {e2:#}"
                    ),
                }
            }
            more => {
                // assume going left to right one of these is a reltag and then we find a blocknum
                // this works, because we don't have plain numbers at least right after reltag in
                // logs. for some definition of "works".

                let Some((reltag_at, reltag)) = more
                    .iter()
                    .map(AsRef::as_ref)
                    .enumerate()
                    .find_map(|(i, s)| {
                        s.split_once("rel=")
                            .map(|(_garbage, actual)| actual)
                            .unwrap_or(s)
                            .parse::<RelTag>()

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Read both causes in the message: the first explains the hex failure, the second the log-line failure; fix whichever form you intended.
  2. For hex, paste the complete key string exactly as the pageserver prints it, not a prefix.
  3. For log lines, ensure both rel= and blkno= markers are present and their values well-formed.

Example fix

# before
pageserver_ctl key "000000067F00010001"
# -> failed to parse ... as hex or span attributes (both errors listed)

# after
pageserver_ctl key "000000067F000100010000AC0000000006"
# or paste the whole log line:
# pageserver_ctl key "whatever{rel=1663/16389/24615 blkno=1052204 req_lsn=FFFFFFFF/FFFFFFFF}"
Defensive patterns

Strategy: validation

Validate before calling

// try the cheap, unambiguous forms first before relying on the ctl's combined parse
if let Ok(key) = Key::from_hex(arg) {
    // unambiguous hex form
} else if arg.contains("rel=") && arg.contains("blkno=") {
    // log-line form; let the ctl parse it
} else {
    anyhow::bail!("argument is neither a hex key nor a log line with rel=/blkno=");
}

Type guard

fn looks_like_key_material(s: &str) -> bool {
    Key::from_hex(s).is_ok() || (s.contains("rel=") && s.contains("blkno="))
}

Prevention

When it happens

Trigger: One argument that is neither a valid full-length hex key nor a log line with parseable rel= and blkno= fields; for example a truncated hex string, or a log line missing blkno.

Common situations: Copying only a prefix of a key from logs; pasting a log line whose rel= value is malformed; extra shell quoting that mangles the argument.

Understand the failure class

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/2ebdad815c33f178. Report an issue: GitHub.