linera-io/linera-protocol · error

Invalid address value: {s}

Error message

Invalid address value: {s}

What it means

AccountOwner::from_str failed to parse the input as any of the three supported owner-address forms. A valid AccountOwner string is '0x' followed by either 64 hex chars (32-byte Address32, a CryptoHash), 40 hex chars (20-byte Address20, an EVM-style address), or exactly 2 hex chars (a Reserved value 0x00-0xFF). Anything else — wrong prefix, odd length, non-hex characters, or a 0x-prefixed string of an unsupported length — reaches the final bail.

Source

Thrown at linera-base/src/identifiers.rs:1242

                    return Ok(AccountOwner::Address32(hash));
                }
            } else if s.len() == 40 {
                let address = hex::decode(s)?;
                if address.len() != 20 {
                    anyhow::bail!("Invalid address length: {s}");
                }
                let address = <[u8; 20]>::try_from(address.as_slice()).unwrap();
                return Ok(AccountOwner::Address20(address));
            }
            if s.len() == 2 {
                let bytes = hex::decode(s)?;
                if bytes.len() == 1 {
                    let value = u8::from_be_bytes(bytes.try_into().expect("one byte"));
                    return Ok(AccountOwner::Reserved(value));
                }
            }
        }
        anyhow::bail!("Invalid address value: {s}");
    }
}

impl fmt::Display for ChainId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Display::fmt(&self.0, f)
    }
}

impl std::str::FromStr for ChainId {
    type Err = CryptoError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(ChainId(CryptoHash::from_str(s)?))
    }
}

impl TryFrom<&[u8]> for ChainId {

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Check the string against the three accepted shapes: ^0x[0-9a-fA-F]{64}$ (Address32), ^0x[0-9a-fA-F]{40}$ (Address20), ^0x[0-9a-fA-F]{2}$ (Reserved); fix length/prefix accordingly.
  2. If you meant an EVM address, ensure it is the full 20-byte hex (40 chars) with the 0x prefix.
  3. If you meant a reserved/system owner, use exactly two hex digits like '0x01'.
  4. If the value comes from user input or a config file, validate with a regex before parsing and surface a field-specific error.

Example fix

// before
let owner: AccountOwner = s.parse()?; // 'Invalid address value: 0x1234'

// after
use regex::Regex;
let re = Regex::new(r"^0x(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{40}|[0-9a-fA-F]{2})$").unwrap();
anyhow::ensure!(re.is_match(s.trim()), "owner must be 0x + 64, 40 or 2 hex chars, got: {s}");
let owner: AccountOwner = s.trim().parse()?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_account_owner_shape(s: &str) -> bool {
    let Some(hex) = s.strip_prefix("0x") else { return false };
    hex.len() == 64 || hex.len() == 40 || (hex.len() == 2 && hex.chars().all(|c| c.is_ascii_hexdigit()))
}

// before parsing user/config input:
anyhow::ensure!(valid_account_owner_shape(input), "malformed AccountOwner: {input}");

Type guard

fn is_valid_account_owner_str(s: &str) -> bool {
    AccountOwner::from_str(s).is_ok()
}

Try / catch

match AccountOwner::from_str(&input) {
    Ok(owner) => owner,
    Err(err) if err.to_string().starts_with("Invalid address value") => {
        return Err(anyhow::anyhow!("owner must be 0x + 64/40/2 hex chars: {input}").into());
    }
    Err(err) => return Err(err.into()),
}

Prevention

When it happens

Trigger: Calling 'AccountOwner::from_str(s)', or deserializing an AccountOwner from a human-readable format (JSON config, CLI argument, GraphQL input), with e.g. '0x1234' (4 hex chars), 'e1e2...'/missing 0x prefix, a 64-char string that is not a valid CryptoHash, an all-lowercase/uppercase issue is fine but a non-hex char like '0xzz' is not, or an EVM checksummed address is fine hex-wise but any typo in length throws.

Common situations: Passing an EVM address with the '0x' prefix removed or truncated; copy-pasting a ChainId (64 hex chars after 0x, which is valid Address32) into a field that was then edited; config files migrated between versions that used different address encodings; mixing up Owner (AccountOwner) and Account (chain+owner pair) string forms.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/d9c023f7345ad134. Report an issue: GitHub.