hasura/graphql-engine · error · Error
Unexpected status code: {0}
Error message
Unexpected status code: {0} What it means
The pre-NDC-response plugin replied with an HTTP status code the engine does not recognize as part of the hook protocol (anything other than the expected success and defined error statuses). The numeric status is included. This means the plugin endpoint is reachable but behaving as a plain HTTP service rather than a hook — e.g. 404 from a wrong path, or 502/503 from an ingress in front of it.
Source
Thrown at v3/crates/plugins/pre-ndc-response-plugin/src/execute.rs:22
use metadata_resolve::{DataConnectorLink, Qualified, ResolvedLifecyclePreNdcResponsePluginHook};
use open_dds::data_connector::DataConnectorName;
use reqwest::{
Client,
header::{InvalidHeaderName, InvalidHeaderValue},
};
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, str::FromStr, sync::Arc};
use tracing_util::{ErrorVisibility, SpanVisibility, TraceableError, set_attribute_on_active_span};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Error while making the HTTP request to the pre-response plugin {0} - {1}")]
ErrorWhileMakingHTTPRequestToTheHook(String, reqwest::Error),
#[error("Error while building the request for the pre-response plugin {0} - {1}")]
BuildRequestError(String, #[source] BuildRequestError),
#[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("Internal error from plugin {plugin_name}")]
PluginInternalError {
plugin_name: String,
error: serde_json::Value,
},
#[error("User error from plugin {plugin_name}")]
PluginUserError {
plugin_name: String,
error: serde_json::Value,
},
}
#[derive(Debug, thiserror::Error)]
pub enum BuildRequestError {
#[error("Invalid header name {header_name}: {error}")]View on GitHub (pinned to 724551b9ae)
Solutions
- Check which status code appears and curl the exact webhook URL to see the raw response
- Correct the path/port in the plugin webhook config
- Bypass or configure auth middleware on the plugin route so hook requests reach the plugin
- Ensure the plugin returns the protocol's expected status codes on success
Defensive patterns
Strategy: validation
Validate before calling
let status = resp.status();
assert!(status.is_success() || EXPECTED_ERROR_STATUSES.contains(&status.as_u16()), "unexpected {}", status); Try / catch
let code = resp.status().as_u16();
if !EXPECTED.contains(&code) {
let body = resp.text().await.unwrap_or_default();
tracing::error!(code, body, "unexpected hook status");
} Prevention
- Point the webhook at the exact hook path
- Don't put auth-protected or rewriting proxies in front of the plugin
When it happens
Trigger: The webhook URL points at the wrong path so the server returns 404, or an ingress/gateway in front of the plugin returns 502/503/504, and the engine maps the unknown code to UnexpectedStatusCode(code).
Common situations: Wrong path or port in plugin webhook config (404s); plugin behind a load balancer that returns 503 during deploys; auth middleware in front of the plugin returning 401/403.
Related errors
- Error while making the HTTP request to the pre-response plug
- Unexpected status code: {0}
- Unexpected status code: {0}
- Unexpected status code: {0}
- Error parsing the request: {0}
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/ed5cd7a0555d61ce.
Report an issue: GitHub.