EpicGames/lore · error · anyhow::Error

presigned_url_hmac_key is not valid hex

Error message

presigned_url_hmac_key is not valid hex: {e}

What it means

At startup, build_presign_config reads settings.presigned_url_hmac_key, which must be a hex-encoded HMAC key for signing S3 presigned URLs. If hex::decode fails on that string, the server aborts with this message. The library validates eagerly so bad signing keys never reach request handling.

Solutions

  1. Hex-encode the key: generate with `openssl rand -hex 32` and set the result verbatim.
  2. Strip whitespace/quotes and remove any '0x' prefix from the configured value.
  3. If the key was stored base64-encoded, convert with `base64 -d | xxd -p -c 64`.
  4. If presigned URLs are not needed, unset the key entirely (build_presign_config returns Ok(None)).

Example fix

// before (config)
presigned_url_hmac_key = "0xdeadbeef"
// after
presigned_url_hmac_key = "deadbeef" // valid hex, >= 32 bytes
Defensive patterns

Strategy: validation

Validate before calling

fn valid_hex_key(key: &str, min_bytes: usize) -> Result<(), String> {
    let k = key.trim().trim_start_matches("0x");
    hex::decode(k)
        .map_err(|e| format!("not valid hex: {e}"))?
        .len()
        .ge(&min_bytes)
        .then_some(())
        .ok_or_else(|| format!("key shorter than {min_bytes} bytes"))
}

Prevention

When it happens

Trigger: Setting presigned_url_hmac_key in the server settings (config file or env) to a value that is not valid hex: odd number of characters, whitespace, '0x' prefix, non-hex characters (g, z, -), or an empty-but-present string.

Common situations: Operators paste a base64 or raw ASCII secret into the config instead of hex; a shell variable carries a trailing newline or quotes; someone prefixes the key with '0x'; YAML/JSON mangling introduces spaces.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/10a8dc4bf3bef98e. Report an issue: GitHub.

Appendix: source

Thrown at lore-server/src/http/server.rs:204

    }
}

/// Renders the resolved allowlist for the startup log.
fn describe_allowed_types(types: &[String]) -> String {
    if types.is_empty() {
        "<none>".to_string()
    } else {
        types.join(", ")
    }
}

fn build_presign_config(settings: &PresignSettings) -> Result<Option<PresignConfig>> {
    let Some(key_hex) = settings.hmac_key.as_deref() else {
        return Ok(None);
    };

    let key_bytes = hex::decode(key_hex)
        .map_err(|e| anyhow::anyhow!("presigned_url_hmac_key is not valid hex: {e}"))?;

    if key_bytes.len() < MIN_HMAC_KEY_BYTES {
        anyhow::bail!(
            "presigned_url_hmac_key must be at least {MIN_HMAC_KEY_BYTES} bytes, got {}",
            key_bytes.len()
        );
    }

    let key_id = blake3::hash(&key_bytes).to_hex()[..16].to_string();
    let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &key_bytes);

    Ok(Some(PresignConfig {
        hmac_key,
        key_id,
        min_ttl_seconds: settings.min_ttl_seconds,
        default_ttl_seconds: settings.default_ttl_seconds,
        max_ttl_seconds: settings.max_ttl_seconds,
        content_type_allowlist: ContentTypeAllowlist::try_from_policy(

View on GitHub (pinned to 074eb0b0d1)