{"record":{"id":"c21a294169c47eb2","repo":"neondatabase/neon","slug":"failed-to-parse-param-name","errorCode":null,"errorMessage":"failed to parse {param_name}","messagePattern":"failed to parse (.+?)","errorType":"http","errorClass":"ApiError","httpStatus":400,"severity":"warning","filePath":"libs/http-utils/src/request.rs","lineNumber":30,"sourceCode":"pub fn get_request_param<'a>(\n    request: &'a Request<Body>,\n    param_name: &str,\n) -> Result<&'a str, ApiError> {\n    match request.param(param_name) {\n        Some(arg) => Ok(arg),\n        None => Err(ApiError::BadRequest(anyhow!(\n            \"no {param_name} specified in path param\",\n        ))),\n    }\n}\n\npub fn parse_request_param<T: FromStr>(\n    request: &Request<Body>,\n    param_name: &str,\n) -> Result<T, ApiError> {\n    match get_request_param(request, param_name)?.parse() {\n        Ok(v) => Ok(v),\n        Err(_) => Err(ApiError::BadRequest(anyhow!(\n            \"failed to parse {param_name}\",\n        ))),\n    }\n}\n\npub fn get_query_param<'a>(\n    request: &'a Request<Body>,\n    param_name: &str,\n) -> Result<Option<Cow<'a, str>>, ApiError> {\n    let query = match request.uri().query() {\n        Some(q) => q,\n        None => return Ok(None),\n    };\n    let values = url::form_urlencoded::parse(query.as_bytes())\n        .filter_map(|(k, v)| if k == param_name { Some(v) } else { None })\n        // we call .next() twice below. If it's None the first time, .fuse() ensures it's None afterwards\n        .fuse();\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/http-utils/src/request.rs#L12-L48","documentation":"Returned as HTTP 400 BadRequest by parse_request_param in neon's http-utils when the path parameter exists but T::from_str fails on its value. The generic is instantiated per endpoint with types like TenantId/TimelineId (UUID-parsing newtypes), NodeId, or unsigned integers, so this is the client-facing 'malformed identifier in the URL' error.","triggerScenarios":"GET /v1/tenant/not-a-uuid/timeline — the :tenant_id segment fails TenantId::from_str; also negative or non-numeric values where u32/u64 is expected (e.g. /v1/node/-1), or a UUID with wrong hyphenation/length.","commonSituations":"Clients passing internal integer IDs where a UUID is required; truncated or whitespace-padded IDs from env vars; copy-paste of timeline IDs missing characters; using a tenant name instead of its ID in the URL.","solutions":["Check the exact value you put in that URL segment against the expected type (canonical UUID for tenant/timeline IDs, non-negative integer for numeric params)","Validate IDs client-side before building the URL (UUID parse for tenant/timeline)","If you truly have a non-UUID identifier, first resolve it to the tenant's ID via the listing endpoints, then use the ID in the path"],"exampleFix":"# before\ncurl http://localhost:9898/v1/tenant/my-tenant/timeline   # 400 failed to parse tenant_id\n\n# after\ncurl http://localhost:9898/v1/tenant/3d982a9f-8b0f-4c2a-b1a6-1f2b6c9ab7ee/timeline","handlingStrategy":"validation","validationCode":"// Validate IDs before building the URL:\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nfunction tenantUrl(base, tenantId) {\n  if (!UUID_RE.test(tenantId)) throw new Error(`invalid tenant id: ${tenantId}`);\n  return `${base}/v1/tenant/${tenantId}`;\n}","typeGuard":"function isUuid(s) {\n  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(String(s));\n}","tryCatchPattern":"// Map the 400 to a clearer client-side message naming the param:\nconst resp = await fetch(url);\nif (resp.status === 400) {\n  const msg = await resp.text();\n  const m = msg.match(/failed to parse (\\w+)/);\n  throw new Error(m ? `malformed ${m[1]} in URL path` : msg);\n}","preventionTips":["Parse-check all IDs (UUID/int) client-side before constructing paths","Use typed ID wrappers (TenantId/TimelineId) in your client code, not raw strings","Resolve names to IDs via listing endpoints instead of guessing path values"],"tags":["neon","http-utils","routing","path-param","uuid","http-400"],"backgroundTag":"invalid-path-parameter","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}