astrid-runtime/astrid · error

signing key must be exactly 32 raw bytes (got )

Error message

signing key {} must be exactly 32 raw bytes (got {})

What it means

`load_signing_key` reads a file expected to contain a raw 32-byte ed25519 secret key and refuses anything else. The library enforces the exact ed25519 seed length before constructing a `KeyPair`; keys of any other size (e.g. base64 text, PEM, or 64-byte expanded keys) are rejected so signing never proceeds with a malformed key. The key file path is never logged to avoid leaking secrets.

Solutions

  1. Regenerate or convert the key to exactly 32 raw bytes (e.g. strip a trailing newline, or re-export the seed portion of a 64-byte key).
  2. If the key is base64/hex encoded, decode it to raw 32 bytes before staging: `base64 -d key.b64 > key.raw`.
  3. Verify the file: `wc -c keyfile` must print 32, and confirm it is the secret key, not the public key.

Example fix

// before
$ head -c 64 expanded.key > signing.key   # 64 bytes -> error
// after
$ head -c 32 expanded.key > signing.key   # raw 32-byte ed25519 seed
Defensive patterns

Strategy: validation

Validate before calling

let bytes = std::fs::read(path)?;
if bytes.len() != 32 {
    return Err(anyhow!("signing key must be 32 raw bytes, got {}", bytes.len()));
}

Type guard

fn is_raw_ed25519_seed(bytes: &[u8]) -> bool { bytes.len() == 32 }

Try / catch

match load_signing_key(path) {
    Ok(kp) => /* sign */,
    Err(e) => eprintln!("check key file is exactly 32 raw bytes: {e:#}"),
}

Prevention

When it happens

Trigger: Calling `load_signing_key(path)` (via `run_seal` or the test) when the file at `path` does not exist, is empty, is a text/PEM/base64-encoded key instead of raw bytes, or is a 64-byte (seed+public) key file.

Common situations: Exporting a key from an SSH/OpenSSL tool that writes PEM, copy-pasting a key into a file (adding trailing newline is fine at 33 bytes — still fails), or pointing at the wrong file such as the public key or a 64-byte pkcs8 artifact.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-cli/src/commands/distro/seal.rs:198

        if candidate.is_file() {
            return Ok(candidate);
        }
        bail!("{} contains no Distro.toml", p.display());
    }
    if p.is_file() {
        return Ok(p.to_path_buf());
    }
    bail!(
        "seal requires a local Distro.toml path or its directory; {distro:?} is not a file or directory"
    )
}

/// Load a 32-byte raw ed25519 secret key from `path`. Never logged.
fn load_signing_key(path: &Path) -> anyhow::Result<astrid_crypto::KeyPair> {
    let bytes = std::fs::read(path)
        .with_context(|| format!("failed to read signing key {}", path.display()))?;
    if bytes.len() != 32 {
        bail!(
            "signing key {} must be exactly 32 raw bytes (got {})",
            path.display(),
            bytes.len()
        );
    }
    astrid_crypto::KeyPair::from_secret_key(&bytes)
        .map_err(|e| anyhow::anyhow!("invalid ed25519 secret key: {e}"))
}

#[cfg(test)]
mod tests {
    use super::*;

    struct CurrentDirGuard(PathBuf);

    impl CurrentDirGuard {
        fn set(path: &Path) -> Self {
            let original = std::env::current_dir().unwrap();

View on GitHub (pinned to affd8760f4)