ducaale/xh · error
message-signature: Failed to create HMAC key
Error message
message-signature: Failed to create HMAC key: {:?} What it means
While building an HMAC-SHA256 shared signing key, the base64-encoded key material is passed to `SharedKey::from_base64`, which validates the encoding. If that call fails (e.g. the material is not valid base64 after re-encoding, or fails key constraints), the error is wrapped with this message via `anyhow!` and propagated up through `build_signing_key`.
Solutions
- Verify the key material is valid base64 of the correct length for HMAC-SHA256; re-encode with `base64 -w0 < keyfile`.
- Check for stray whitespace, quotes, or newlines in the value passed via `--unstable-m-sig-key` or the env var.
- Confirm the key is raw key bytes (not PEM/hex); convert appropriately before passing it.
Example fix
// before xh --unstable-m-sig-id=k1 --unstable-m-sig-key="$(cat key.pem)" POST https://api.example.com // after xh --unstable-m-sig-id=k1 --unstable-m-sig-key="$(base64 -w0 key.bin)" POST https://api.example.com
Defensive patterns
Strategy: validation
Validate before calling
# validate base64 key material before use:
echo "$KEY" | base64 -d >/dev/null 2>&1 || { echo 'key is not valid base64'; exit 1; } Try / catch
// Rust caller:
match build_signing_key_with_algorithm(...) {
Err(e) if e.to_string().contains("Failed to create HMAC key") => {
eprintln!("check key encoding: must be valid base64 raw key bytes");
}
other => other?,
} Prevention
- Re-encode keys with `base64 -w0` before passing them.
- Strip whitespace/newlines from secret-manager output.
- Know the expected key format (raw bytes, base64) — never PEM or hex unless documented.
When it happens
Trigger: Passing `--unstable-m-sig-key` material that `SharedKey::from_base64` rejects for the HmacSha256 algorithm — e.g. malformed base64 content or an invalid key length for the algorithm.
Common situations: Pasting a key with whitespace/newlines; supplying a PEM or hex key where raw/base64 material is expected; truncating a key when copying it from a secret manager.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13).
Data as JSON: /api/errors/b6de6fc580d63286.
Report an issue: GitHub.
Appendix: source
Thrown at src/message_signature.rs:290
"message-signature: RSA private keys require an explicit algorithm. Use --unstable-m-sig-alg=rsa-v1_5-sha256 or --unstable-m-sig-alg=rsa-pss-sha512"
);
}
bail!(
"message-signature: Failed to parse PEM private key. Supported algorithms: ed25519, ecdsa-p256-sha256, ecdsa-p384-sha384, rsa-v1_5-sha256, rsa-pss-sha512"
);
}
}
build_hmac_signing_key(key_material, key_id)
}
fn build_hmac_signing_key(
key_material: &[u8],
key_id: &str,
) -> Result<(MessageSigningKey, AlgorithmName)> {
let encoded = STANDARD.encode(key_material);
let shared_key = SharedKey::from_base64(&AlgorithmName::HmacSha256, &encoded)
.map_err(|e| anyhow!("message-signature: Failed to create HMAC key: {:?}", e))?;
Ok((
MessageSigningKey::Shared(shared_key, key_id.to_string()),
AlgorithmName::HmacSha256,
))
}
fn build_signing_key_with_algorithm(
key_material: &[u8],
key_id: &str,
algorithm: &AlgorithmName,
) -> Result<(MessageSigningKey, AlgorithmName)> {
if algorithm == &AlgorithmName::HmacSha256 {
return build_hmac_signing_key(key_material, key_id);
}
let secret = if let Ok(pem) = std::str::from_utf8(key_material) {
if pem.contains("-----BEGIN") {
SecretKey::from_pem(algorithm, pem).with_context(|| {View on GitHub (pinned to 2404aceecc)