ducaale/xh · error
message-signature: Duplicate covered component identifier
Error message
message-signature: Duplicate covered component identifier: {} What it means
During HTTP message signature construction (RFC 9421), the covered-components list must contain each component identifier at most once. build_signature_params compares semantic uniqueness keys (parameter order ignored) and bails if the same component id appears twice.
Solutions
- Deduplicate the covered-components list before signing
- Compare components by their semantic uniqueness key, ignoring parameter order
- Reject or merge duplicate component specs at configuration load time
Example fix
// before let components = vec!["@method", "@target-uri", "@method"]; sign_request(&req, &components, ...)?; // after let components: Vec<_> = dedup_by_uniqueness_key(vec!["@method", "@target-uri", "@method"]); sign_request(&req, &components, ...)?;
Defensive patterns
Strategy: validation
Validate before calling
// validate covered components before sign_request
fn has_duplicates(ids: &[&str]) -> bool {
let mut seen = std::collections::HashSet::new();
ids.iter().any(|id| !seen.insert(normalize_component_id(id)))
}
if has_duplicates(&components) { eprintln!("duplicate covered component"); } Try / catch
match sign_request(&req, &components, &key) {
Ok(signed) => /* ... */,
Err(e) if e.to_string().contains("Duplicate covered component") => {
eprintln!("deduplicate covered components and retry");
}
Err(e) => return Err(e),
} Prevention
- Deduplicate the covered-components list at config load time
- Treat parameter-order variants of the same id as equal
- Add unit tests asserting uniqueness of component lists
- Build component lists programmatically with a set
When it happens
Trigger: Calling sign_request (directly or via tests) with a covered-components list containing a repeated identifier, even with different parameter ordering that is semantically equivalent.
Common situations: Hand-written @method/@target-uri lists that duplicate an entry; programmatic assembly that appends default components already present; config merging that concatenates component lists.
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
- Invalid Content-Range
- Content-Range has wrong end
- message-signature: RSA private keys require an explicit…
- message-signature: Failed to parse PEM private key…
- Connection timeout is negative
AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13).
Data as JSON: /api/errors/7541270576e3de10.
Report an issue: GitHub.
Appendix: source
Thrown at src/message_signature.rs:157
HeaderValue::from_str(&value)?,
);
}
Ok(())
}
fn build_signature_params(components: &[String]) -> Result<HttpSignatureParams> {
let mut component_ids = Vec::new();
let mut seen = HashSet::new();
for c in components {
let normalized = normalize_component_id(c);
let id = HttpMessageComponentId::try_from(normalized.as_str())
.with_context(|| format!("message-signature: Invalid component: {}", c))?;
// RFC 9421 requires each covered component identifier to appear at most once.
// Equivalence is based on component id semantics, where parameter order does
// not create a distinct identifier.
let uniqueness_key = component_uniqueness_key(&id);
if !seen.insert(uniqueness_key) {
bail!(
"message-signature: Duplicate covered component identifier: {}",
id
);
}
component_ids.push(id);
}
HttpSignatureParams::try_new(&component_ids)
.context("message-signature: Failed to create signature params")
}
/// Build a canonical key for RFC 9421 component-identifier uniqueness checks.
///
/// RFC 9421 treats component identifiers as unique entries in covered components,
/// and two identifiers that differ only by parameter ordering are equivalent.
/// We normalize:
/// - component name (`HttpField` lowercased, derived names preserved), and
/// - parameters (sorted, then joined),
///View on GitHub (pinned to 2404aceecc)