rustfs/rustfs · warning · SignV2Error

failed to encode query parameters: {reason}

Error message

failed to encode query parameters: {reason}

What it means

SignV2Error::QueryEncode is returned by pre_sign_v2_inner at request_signature_v2.rs:133-136 when serde_urlencoded::to_string fails to serialize the query map (original query params plus AWSAccessKeyId/Expires, or GoogleAccessId for .storage.googleapis.com hosts). For a HashMap<String,String> the form-serializer percent-encodes every string, so a failure has no realistic input trigger. The variant is defensive plumbing that turns a serializer error into a typed failure.

Source

Thrown at crates/signer/src/request_signature_v2.rs:48

// SHA-1 is considered weak, but it's only used for HMAC (not signature collision).
// Migration plan (not yet implemented):
// Phase 1: Support both SHA-1 and SHA-256 (configurable)
// Phase 2: Deprecation warnings in response headers
// Phase 3: Default to SHA-256, SHA-1 becomes optional
// See https://github.com/rustfs/backlog/issues/747 for discussion.

const _SIGN_V4_ALGORITHM: &str = "AWS4-HMAC-SHA256";
const SIGN_V2_ALGORITHM: &str = "AWS";

#[derive(Debug, thiserror::Error)]
pub enum SignV2Error {
    #[error("invalid UTF-8 header value for `{name}`")]
    InvalidHeaderValue { name: String },
    #[error("failed to format signing timestamp: {reason}")]
    TimeFormat { reason: String },
    #[error("failed to build signing timestamp: {reason}")]
    TimeComponent { reason: String },
    #[error("failed to encode query parameters: {reason}")]
    QueryEncode { reason: String },
    #[error("failed to parse uri: {reason}")]
    InvalidUri { reason: String },
    #[error("failed to build uri from parts: {reason}")]
    InvalidUriParts { reason: String },
    #[error("failed to convert canonical headers to UTF-8: {reason}")]
    CanonicalUtf8 { reason: String },
    #[error("failed to parse header value for `{name}`: {reason}")]
    HeaderValueParse { name: String, reason: String },
    #[error("failed to resolve host address: {0}")]
    HostAddr(#[from] HostAddrError),
}

#[derive(Debug)]
struct SignV2Failure {
    request: request::Request<Body>,
    error: SignV2Error,
}

View on GitHub (pinned to 35af688cd9)

Solutions

  1. Verify the rustfs-signer version in the build matches the source you are reading (cargo tree -p rustfs-signer).
  2. Sanitize the incoming query string before presigning: drop pairs that fail UTF-8 or contain control characters.
  3. Switch to try_pre_sign_v2 so the typed reason string from serde_urlencoded is surfaced instead of warn! (line 170) plus an unsigned request.

Example fix

// before: non-UTF-8 bytes carried in from a raw query string
let query: HashMap<String, String> = serde_urlencoded::from_str(query_source).unwrap_or_default();

// after: filter undecodable pairs before they reach the serializer
let query: HashMap<String, String> = serde_urlencoded::from_str(query_source)
    .unwrap_or_default()
    .into_iter()
    .filter(|(k, v)| std::str::from_utf8(k.as_bytes()).is_ok() && std::str::from_utf8(v.as_bytes()).is_ok())
    .collect();
Defensive patterns

Strategy: try-catch

Validate before calling

let ok = serde_urlencoded::from_str::<HashMap<String, String>>(req.uri().query().unwrap_or(""))
    .map(|m| m.keys().all(|k| k.is_ascii()) && m.values().all(|v| v.is_ascii()))
    .unwrap_or(true);

Type guard

fn is_query_encode(e: &SignV2Error) -> bool {
    matches!(e, SignV2Error::QueryEncode { .. })
}

Try / catch

match try_pre_sign_v2(req, ak, sk, 60, vhost) {
    Ok(signed) => signed,
    Err(SignV2Error::QueryEncode { reason }) => {
        // serializer failure on String pairs is defensive: report, do not retry with edited params
        return Err(anyhow!("query serialization failed: {reason}"));
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling try_pre_sign_v2/pre_sign_v2; the serializer would have to reject a String key or value, which form_urlencoded does not do for any &str. Only a modified signer or corrupted in-memory map (non-UTF-8 String contents constructed via unsafe) could reach it.

Common situations: Almost always seen while matching exhaustively on SignV2Error, not at runtime. If it does fire, suspect a stale or locally patched rustfs-signer version rather than your query string.

Related errors


AI-assisted analysis of rustfs/rustfs@35af688cd9 (2026-08-20). Data as JSON: /api/errors/c6847fb4825c218f. Report an issue: GitHub.