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
- Generate correctly: export ZEROCLAW_AUDIT_SIGNING_KEY="$(openssl rand -hex 32)".
- Verify length before launch: echo -n "$ZEROCLAW_AUDIT_SIGNING_KEY" | wc -c must print 64.
- 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.
- 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
- Generate keys only with `openssl rand -hex 32` and store them in a secret manager.
- Add a pre-start check that fails deployment when sign_events=true but the key is not 64 hex chars.
- Archive old keys on rotation so historical audit logs remain verifiable.
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
- Edge TTS binary_path must be a bare command name without pat
- webhook channel requires a `secret` configured for request a
- Refusing to transmit sensitive data over non-HTTPS URL: URL
- security.nevis: {msg}
- Serial path not allowed: {}. Allowed: {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/959f1249d11d92bb.
Report an issue: GitHub.