astrid-runtime/astrid · error

keypair name contains invalid chars; only a-z, 0-9, '-' are…

Error message

keypair name {name:?} contains invalid chars; only a-z, 0-9, '-' are allowed

What it means

validate_name rejects keypair names containing characters outside lowercase ASCII letters, digits, and '-'. This mirrors principal-id formatting and prevents path-separator or shell-metacharacter surprises when the name is used in file paths.

Solutions

  1. Rename the keypair using only a-z, 0-9, and '-' (e.g. "my_key" -> "my-key")
  2. Check for accidental whitespace or invisible characters (trim the input)
  3. Replace '.' or '_' separators with '-'

Example fix

// before
run_show("My_Key.prod")?
// after
run_show("my-key-prod")?
Defensive patterns

Strategy: validation

Validate before calling

fn valid_keypair_name(name: &str) -> bool {
    !name.is_empty()
        && name.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
}

Type guard

fn is_slug(name: &str) -> bool { !name.is_empty() && name.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-') }

Try / catch

match run_show(&name) {
    Err(e) if e.to_string().contains("invalid chars") => eprintln!("use only a-z, 0-9, '-'"),
    other => other,
}

Prevention

When it happens

Trigger: Any keypair command (generate/show/pubkey/delete) or a public-key load / binding record where the name contains uppercase letters, underscores, spaces, dots, slashes, or other non-allowed characters.

Common situations: Using snake_case names like "my_key"; CamelCase names; names copied with a trailing space; names containing dots or '/'.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/e34cdf7f810c12af. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/keypair.rs:591

// ── Misc helpers ─────────────────────────────────────────────────

fn validate_name(name: &str) -> Result<()> {
    if name.is_empty() {
        bail!("keypair name must not be empty");
    }
    if name.len() > MAX_NAME_LEN {
        bail!(
            "keypair name must be at most {MAX_NAME_LEN} chars (got {})",
            name.len()
        );
    }
    // Lowercase letters, digits, dash. Same posture as principal ids;
    // also avoids path-separator / shell-meta surprises.
    if !name
        .chars()
        .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
    {
        bail!("keypair name {name:?} contains invalid chars; only a-z, 0-9, '-' are allowed");
    }
    Ok(())
}

fn default_name() -> String {
    let mut bytes = [0u8; 4];
    SysRng
        .try_fill_bytes(&mut bytes)
        .expect("OS CSPRNG unavailable while generating default keypair name");
    format!("key-{}", hex::encode(bytes))
}

fn fingerprint_pubkey(hex_pub: &str) -> Result<String> {
    PublicKeyFingerprint::from_ed25519_hex(hex_pub)
        .map(PublicKeyFingerprint::into_inner)
        .map_err(|e| anyhow::anyhow!("fingerprint Ed25519 public key: {e}"))
}

View on GitHub (pinned to affd8760f4)