{"record":{"id":"2b0933e7b4351945","repo":"neondatabase/neon","slug":"cannot-parse-query-param-param-name-e","errorCode":null,"errorMessage":"cannot parse query param {param_name}: {e}","messagePattern":"cannot parse query param (.+?): (.+?)","errorType":"http","errorClass":"ApiError","httpStatus":400,"severity":"error","filePath":"libs/http-utils/src/request.rs","lineNumber":92,"sourceCode":"}\n\npub fn must_get_query_param<'a>(\n    request: &'a Request<Body>,\n    param_name: &str,\n) -> Result<Cow<'a, str>, ApiError> {\n    get_query_param(request, param_name)?.ok_or_else(|| {\n        ApiError::BadRequest(anyhow!(\"no {param_name} specified in query parameters\"))\n    })\n}\n\npub fn parse_query_param<E: fmt::Display, T: FromStr<Err = E>>(\n    request: &Request<Body>,\n    param_name: &str,\n) -> Result<Option<T>, ApiError> {\n    get_query_param(request, param_name)?\n        .map(|v| {\n            v.parse().map_err(|e| {\n                ApiError::BadRequest(anyhow!(\"cannot parse query param {param_name}: {e}\"))\n            })\n        })\n        .transpose()\n}\n\npub fn must_parse_query_param<E: fmt::Display, T: FromStr<Err = E>>(\n    request: &Request<Body>,\n    param_name: &str,\n) -> Result<T, ApiError> {\n    parse_query_param(request, param_name)?.ok_or_else(|| {\n        ApiError::BadRequest(anyhow!(\"no {param_name} specified in query parameters\"))\n    })\n}\n\npub async fn ensure_no_body(request: &mut Request<Body>) -> Result<(), ApiError> {\n    match request.body_mut().data().await {\n        Some(_) => Err(ApiError::BadRequest(anyhow!(\"Unexpected request body\"))),\n        None => Ok(()),","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/http-utils/src/request.rs#L74-L110","documentation":"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).","triggerScenarios":"'?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.","commonSituations":"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.","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"],"exampleFix":"# before\ncurl 'http://localhost:9898/v1/tenant?limit=0x10&tenant_id='\n# -> 400 cannot parse query param limit: invalid digit found in string\n\n# after\ncurl 'http://localhost:9898/v1/tenant?limit=16'\n# -> 200","handlingStrategy":"validation","validationCode":"// Validate the raw value yourself to produce a friendlier message:\nlet raw = get_query_param(request, \"limit\")?;\nlet limit: Option<u32> = match raw {\n    Some(v) => match v.parse::<u32>() {\n        Ok(n) => Some(n),\n        Err(_) => {\n            return Err(ApiError::BadRequest(anyhow::anyhow!(\n                \"query param 'limit' must be a non-negative integer, got {v:?}\"\n            )))\n        }\n    },\n    None => None,\n};","typeGuard":"/// True when `s` parses as the endpoint's expected type (u32 here).\nfn is_valid_limit(s: &str) -> bool {\n    !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()) && s.parse::<u32>().is_ok()\n}","tryCatchPattern":"match parse_query_param::<_, u32>(request, \"limit\") {\n    Ok(v) => v,\n    Err(ApiError::BadRequest(e)) if e.to_string().contains(\"cannot parse query param limit\") => {\n        // 400 with the original message; it already includes the FromStr reason\n        return Err(ApiError::BadRequest(e));\n    }\n    Err(other) => return Err(other),\n}","preventionTips":["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"],"tags":["rust","http","query-params","parsing","bad-request"],"backgroundTag":"query-parameter-parse-error","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}