neondatabase/neon · error · ApiError
no {param_name} specified in path param
Error message
no {param_name} specified in path param What it means
Returned as HTTP 400 BadRequest by get_request_param in neon's http-utils when routerify's request.param(param_name) yields None, i.e. the named path parameter was not captured for this request. Because routerify only populates params for patterns that matched, in practice this fires when the handler is invoked on a route whose pattern does not actually define that :param (a route-wiring bug), rather than from a client supplying a wrong value.
Source
Thrown at libs/http-utils/src/request.rs:18
use core::fmt;
use std::borrow::Cow;
use std::str::FromStr;
use anyhow::anyhow;
use hyper::body::HttpBody;
use hyper::{Body, Request};
use routerify::ext::RequestExt;
use super::error::ApiError;
pub fn get_request_param<'a>(
request: &'a Request<Body>,
param_name: &str,
) -> Result<&'a str, ApiError> {
match request.param(param_name) {
Some(arg) => Ok(arg),
None => Err(ApiError::BadRequest(anyhow!(
"no {param_name} specified in path param",
))),
}
}
pub fn parse_request_param<T: FromStr>(
request: &Request<Body>,
param_name: &str,
) -> Result<T, ApiError> {
match get_request_param(request, param_name)?.parse() {
Ok(v) => Ok(v),
Err(_) => Err(ApiError::BadRequest(anyhow!(
"failed to parse {param_name}",
))),
}
}
pub fn get_query_param<'a>(View on GitHub (pinned to 8f60b04da4)
Solutions
- Compare the route pattern string with the param_name passed to get_request_param — they must match exactly, including the leading colon segment name
- Fix the route definition (or the handler's param name) so the pattern declares :<param_name>
- Add a route-level test that hits the endpoint and asserts the param resolves
Example fix
// before
Router::new()
.get("/v1/tenant", tenant_handler) // handler calls get_request_param(req, "tenant_id")
// after
Router::new()
.get("/v1/tenant/:tenant_id", tenant_handler) Defensive patterns
Strategy: type-guard
Validate before calling
// Add a route test that fails when the pattern loses its param:
#[tokio::test]
async fn tenant_route_has_param() {
let resp = router_get("/v1/tenant/abc").await;
assert_ne!(resp.status(), 400, "tenant_id param missing from route pattern");
} Type guard
// In middleware, fail loudly on handlers requesting params the pattern lacks:
fn route_defines_param(route: &str, name: &str) -> bool {
route.split('/').any(|seg| seg == &format!(":{name}"))
} Prevention
- Derive handler param names from the route pattern constants instead of duplicating strings
- Prefer compile-time route builders that force the pattern and the param name to match
- Add one integration test per route asserting path params resolve
When it happens
Trigger: A handler registered on /v1/tenant calls get_request_param(req, "tenant_id") while the route is defined as /v1/tenants (no :tenant_id segment); or a route refactor renamed the pattern segment (e.g. :tenantId vs :tenant_id) so lookup by the old name misses.
Common situations: Route registration refactors where the pattern and the param name in the handler drift apart; copy-pasting a handler to a new route that lacks the segment; routerify middleware re-creating requests without params; adding a new endpoint and forgetting the path parameter in the route string.
Related errors
- failed to parse {param_name}
- invalid format {format}
- duration must be 1-60 secs
- frequency must be <=1000 Hz
- heap profiling not enabled
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/8ed607f44b7ac622.
Report an issue: GitHub.