neondatabase/neon · error · ApiError
cannot parse query param {param_name}: {e}
Error message
cannot parse query param {param_name}: {e} What it means
Thrown by parse_query_param when the query parameter is present but its string value fails FromStr parsing into the target type T. The {e} suffix is the underlying parse error (for example 'invalid digit found in string' or a UUID/Lsn parse message), so the error identifies both the parameter name and why parsing failed. Surfaces as ApiError::BadRequest (HTTP 400).
Source
Thrown at libs/http-utils/src/request.rs:92
}
pub fn must_get_query_param<'a>(
request: &'a Request<Body>,
param_name: &str,
) -> Result<Cow<'a, str>, ApiError> {
get_query_param(request, param_name)?.ok_or_else(|| {
ApiError::BadRequest(anyhow!("no {param_name} specified in query parameters"))
})
}
pub fn parse_query_param<E: fmt::Display, T: FromStr<Err = E>>(
request: &Request<Body>,
param_name: &str,
) -> Result<Option<T>, ApiError> {
get_query_param(request, param_name)?
.map(|v| {
v.parse().map_err(|e| {
ApiError::BadRequest(anyhow!("cannot parse query param {param_name}: {e}"))
})
})
.transpose()
}
pub fn must_parse_query_param<E: fmt::Display, T: FromStr<Err = E>>(
request: &Request<Body>,
param_name: &str,
) -> Result<T, ApiError> {
parse_query_param(request, param_name)?.ok_or_else(|| {
ApiError::BadRequest(anyhow!("no {param_name} specified in query parameters"))
})
}
pub async fn ensure_no_body(request: &mut Request<Body>) -> Result<(), ApiError> {
match request.body_mut().data().await {
Some(_) => Err(ApiError::BadRequest(anyhow!("Unexpected request body"))),
None => Ok(()),View on GitHub (pinned to 8f60b04da4)
Solutions
- Read the {e} suffix and correct the value so FromStr of the target type accepts it
- For empty values, omit the parameter entirely rather than sending '?param='
- Strip shell/template quoting and whitespace from the value before it reaches the URL
- If empty-string should be legal for your endpoint, branch on it explicitly before calling must_parse_query_param
Example fix
# before curl 'http://localhost:9898/v1/tenant?limit=0x10&tenant_id=' # -> 400 cannot parse query param limit: invalid digit found in string # after curl 'http://localhost:9898/v1/tenant?limit=16' # -> 200
Defensive patterns
Strategy: validation
Validate before calling
// Validate the raw value yourself to produce a friendlier message:
let raw = get_query_param(request, "limit")?;
let limit: Option<u32> = match raw {
Some(v) => match v.parse::<u32>() {
Ok(n) => Some(n),
Err(_) => {
return Err(ApiError::BadRequest(anyhow::anyhow!(
"query param 'limit' must be a non-negative integer, got {v:?}"
)))
}
},
None => None,
}; Type guard
/// True when `s` parses as the endpoint's expected type (u32 here).
fn is_valid_limit(s: &str) -> bool {
!s.is_empty() && s.chars().all(|c| c.is_ascii_digit()) && s.parse::<u32>().is_ok()
} Try / catch
match parse_query_param::<_, u32>(request, "limit") {
Ok(v) => v,
Err(ApiError::BadRequest(e)) if e.to_string().contains("cannot parse query param limit") => {
// 400 with the original message; it already includes the FromStr reason
return Err(ApiError::BadRequest(e));
}
Err(other) => return Err(other),
} Prevention
- Document the exact grammar next to every query param (uuid, u32, hex Lsn '0/HHHH') in the route table
- In clients, serialize params through typed form encoders instead of string interpolation
- Reject or trim empty strings client-side: prefer omitting the param over sending '?p='
- Add property tests feeding garbage values through parse_query_param to keep error messages actionable
When it happens
Trigger: '?limit=abc' parsed as u32; '?generation=-5' parsed as an unsigned type; '?lsn=41C/0' missing the leading '0/' expected by Lsn::from_str; an empty value '?tenant_id=' parsed as Uuid; any handler using parse_query_param / must_parse_query_param with a FromStr type.
Common situations: Passing floats where an integer type is expected ('?max=10.5'); mixing decimal and hex LSN formats; URL-encoding artifacts like '+' left inside a UUID; copy-pasting values with quotes or trailing whitespace from tickets or docs; i8 overflow for zstd-style level parameters.
Related errors
- param {param_name} specified more than once
- no {param_name} specified in query parameters
- Unexpected request body
- could not parse spec
- error downloading extension {:?}: {:?}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/2b0933e7b4351945.
Report an issue: GitHub.