zeroclaw-labs/zeroclaw · error

ZEROCLAW_AUDIT_SIGNING_KEY must be 32 bytes (64 hex chars),

Error message

ZEROCLAW_AUDIT_SIGNING_KEY must be 32 bytes (64 hex chars), got {}

What it means

ZEROCLAW_AUDIT_SIGNING_KEY decoded from hex successfully, but the result is not exactly 32 bytes. The audit chain signs entries with HMAC-SHA256, which here requires a 32-byte key — i.e. exactly 64 hex characters. Construction of the audit log with sign_events=true fails at startup.

Source

Thrown at crates/zeroclaw-runtime/src/security/audit.rs:273

                    ),
                }
            })?;

            let key_bytes = hex::decode(&key_hex).map_err(|e| {
                ::zeroclaw_log::record!(
                    ERROR,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                        .with_attrs(::serde_json::json!({"error": format!("{e}")})),
                    "audit log: ZEROCLAW_AUDIT_SIGNING_KEY env var must be hex-encoded"
                );
                anyhow::Error::msg(format!(
                    "ZEROCLAW_AUDIT_SIGNING_KEY must be hex-encoded: {e}"
                ))
            })?;

            if key_bytes.len() != 32 {
                bail!(
                    "ZEROCLAW_AUDIT_SIGNING_KEY must be 32 bytes (64 hex chars), got {}",
                    key_bytes.len()
                );
            }

            Some(key_bytes)
        } else {
            None
        };

        let log_path = zeroclaw_dir.join(&config.log_path);
        let chain_state = recover_chain_state(&log_path);
        Ok(Self {
            log_path,
            config,
            chain: Mutex::new(chain_state),
            signing_key,
        })

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Generate correctly: export ZEROCLAW_AUDIT_SIGNING_KEY="$(openssl rand -hex 32)".
  2. Verify length before launch: echo -n "$ZEROCLAW_AUDIT_SIGNING_KEY" | wc -c must print 64.
  3. If a wrong-length key already signed records, note those records cannot be verified later — keep the original key archived for verification of old logs.
  4. Store the key in a secret manager or unit Environment= with the exact 64-hex-char value.

Example fix

# before
export ZEROCLAW_AUDIT_SIGNING_KEY="my-secret-key"          # not hex, wrong length
export ZEROCLAW_AUDIT_SIGNING_KEY="$(openssl rand -hex 16)" # 32 hex chars = 16 bytes

# after
export ZEROCLAW_AUDIT_SIGNING_KEY="$(openssl rand -hex 32)" # 64 hex chars = 32 bytes
Defensive patterns

Strategy: validation

Validate before calling

fn signing_key_env_valid() -> bool {
    std::env::var("ZEROCLAW_AUDIT_SIGNING_KEY")
        .map(|v| v.len() == 64 && v.bytes().all(|b| b.is_ascii_hexdigit()))
        .unwrap_or(false)
}

Try / catch

if !signing_key_env_valid() && config.sign_events {
    anyhow::bail!("ZEROCLAW_AUDIT_SIGNING_KEY must be 64 hex chars before enabling sign_events");
}

Prevention

When it happens

Trigger: Setting the env var to a hex string of the wrong length (e.g. 128 hex chars = 64 bytes, or 32 hex chars = 16 bytes); pasting a passphrase that happens to be valid hex; truncating or re-generating the key incorrectly after rotation.

Common situations: Key generated with `openssl rand -hex 16` (32 hex chars) instead of `-hex 32`; copy-paste losing characters; switching from another tool's base64 key without re-encoding; rotation scripts emitting a different length.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/959f1249d11d92bb. Report an issue: GitHub.