hasura/graphql-engine · error · Error

Invalid header name {0}

Error message

Invalid header name {0}

What it means

The pre-route hook supplied a header name that is not a valid HTTP header name (invalid characters, empty, or malformed), and the executor rejected it while applying hook instructions to the request. The offending name is included as {0}.

Source

Thrown at v3/crates/plugins/pre-route-plugin/src/execute.rs:34

use tracing_util::{
    ErrorVisibility, SpanVisibility, Traceable, TraceableError, set_attribute_on_active_span,
};

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Error while making the HTTP request to the pre-parse plugin {0} - {1}")]
    ErrorWhileMakingHTTPRequestToTheHook(String, reqwest::Error),
    #[error("Error while building the request for the pre-parse plugin {0} - {1}")]
    BuildRequestError(String, String),
    #[error("Reqwest error: {0}")]
    ReqwestError(reqwest::Error),
    #[error("Unexpected status code: {0}")]
    UnexpectedStatusCode(u16),
    #[error("Error parsing the request: {0}")]
    PluginRequestParseError(serde_json::error::Error),
    #[error("HTTP method {0} not supported")]
    UnsupportedHTTPMethod(String),
    #[error("Invalid header name {0}")]
    InvalidHeaderName(String),
    #[error("Invalid header value {0}")]
    InvalidHeaderValue(String),
    #[error("Not found")]
    NotFound,
    // Only used in the pre-route plugin handler function. Defined to ensure consistent
    // response formatting in IntoResponse impl.
    #[error("Cannot load pre-route plugins: {0}")]
    CannotLoadPlugins(String),
}

impl TraceableError for Error {
    fn visibility(&self) -> ErrorVisibility {
        ErrorVisibility::Internal
    }
}

impl IntoResponse for Error {

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Validate header names in the hook with http::HeaderName::from_str before returning them.
  2. Whitelist allowed header names the hook may set.
  3. Trim input and reject names containing spaces, colons, or control characters.
  4. Log the offending name from the error message to locate the source.

Example fix

// before (hook)
instructions.push(HeaderOp { name: user_input, value });

// after (hook)
let name = http::HeaderName::from_str(user_input.trim())
    .map_err(|_| Error::InvalidHeaderName(user_input.into()))?;
instructions.push(HeaderOp { name: name.to_string(), value });
Defensive patterns

Strategy: validation

Validate before calling

fn valid_header_name(name: &str) -> bool {
    !name.is_empty() && http::HeaderName::from_str(name).is_ok()
}

Type guard

fn is_valid_header_name(n: &str) -> bool {
    http::HeaderName::from_str(n.trim()).is_ok()
}

Try / catch

if let Error::InvalidHeaderName(name) = &e { tracing::warn!("dropping invalid header from hook: {name}"); }

Prevention

When it happens

Trigger: The hook's response instructs the engine to add/override a header with a name containing spaces, non-ASCII characters, or CR/LF — anything http::HeaderName::from_str would reject.

Common situations: Hook passing through unvalidated user input as header names; header name typos; header-injection attempts or accidental newlines in config-driven header maps.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/bbe958426f9b96b5. Report an issue: GitHub.