{"record":{"id":"b07901cef1716c24","repo":"neondatabase/neon","slug":"invalid-connstring-err","errorCode":null,"errorMessage":"invalid connstring: {err}","messagePattern":"invalid connstring: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pageserver/pagebench/src/cmd/basebackup.rs","lineNumber":162,"sourceCode":"            loop {\n                let start = std::time::Instant::now();\n                tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n                let completed_requests = stats.completed_requests.swap(0, Ordering::Relaxed);\n                let elapsed = start.elapsed();\n                info!(\n                    \"RPS: {:.0}\",\n                    completed_requests as f64 / elapsed.as_secs_f64()\n                );\n            }\n        }\n    });\n\n    let mut work_senders = HashMap::new();\n    let mut tasks = Vec::new();\n    let scheme = match Url::parse(&args.page_service_connstring) {\n        Ok(url) => url.scheme().to_lowercase().to_string(),\n        Err(url::ParseError::RelativeUrlWithoutBase) => \"postgresql\".to_string(),\n        Err(err) => return Err(anyhow!(\"invalid connstring: {err}\")),\n    };\n    for &tl in &timelines {\n        let (sender, receiver) = tokio::sync::mpsc::channel(1); // TODO: not sure what the implications of this are\n        work_senders.insert(tl, sender);\n\n        let client: Box<dyn Client> = match scheme.as_str() {\n            \"postgresql\" | \"postgres\" => Box::new(\n                LibpqClient::new(&args.page_service_connstring, tl, !args.no_compression).await?,\n            ),\n            \"grpc\" => Box::new(\n                GrpcClient::new(&args.page_service_connstring, tl, !args.no_compression).await?,\n            ),\n            scheme => return Err(anyhow!(\"invalid scheme {scheme}\")),\n        };\n\n        tasks.push(tokio::spawn(run_worker(\n            client,\n            Arc::clone(&start_work_barrier),","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/pagebench/src/cmd/basebackup.rs#L144-L180","documentation":"pagebench's basebackup command parses --page-service-connstring with Url::parse. The case RelativeUrlWithoutBase (a bare string with no scheme) is tolerated and defaults to the postgresql transport, but any other parse failure returns 'invalid connstring: {err}' with the url crate's reason (invalid port number, invalid IPv6 literal, invalid domain character, empty host). The string never reaches the pageserver, so this is purely client-side argument validation.","triggerScenarios":"Running pagebench basebackup with a connstring containing URL syntax errors: non-numeric or out-of-range port (postgres://host:99999), an IPv6 literal without brackets (postgres://::1:6400), spaces or control characters, bad percent-encoding, or a truncated URL such as 'postgres://' with no host.","commonSituations":"Typos when copying the page_service endpoint out of configs or logs; shell scripts interpolating empty or unquoted variables; documentation placeholders like postgresql://user@<host>:6400 left in the string.","solutions":["Rewrite the connstring in full URL form: postgresql://user@host:port for the libpq page protocol or grpc://host:port for the gRPC transport","Check that the port is numeric and within 0-65535 and the host contains no spaces or placeholder characters","Shell-quote the argument and echo it back to spot interpolation problems","If you meant a bare host:port, note that only RelativeUrlWithoutBase falls back to postgresql; most partial strings fail parsing or hit the separate 'invalid scheme' error, so always pass the explicit scheme"],"exampleFix":"# before\npagebench basebackup --page-service-connstring 'postgres://localhost:notaport'\n# after\npagebench basebackup --page-service-connstring 'postgres://localhost:6400'","handlingStrategy":"validation","validationCode":"fn validate_connstring(s: &str) -> anyhow::Result<String> {\n    match url::Url::parse(s) {\n        Ok(u) => Ok(u.scheme().to_lowercase()),\n        Err(url::ParseError::RelativeUrlWithoutBase) => Ok(\"postgresql\".to_string()),\n        Err(e) => anyhow::bail!(\"invalid connstring: {e}\"),\n    }\n}\n\n// run before starting the bench\nlet scheme = validate_connstring(&args.page_service_connstring)?;","typeGuard":"fn is_parseable_connstring(s: &str) -> bool {\n    matches!(\n        url::Url::parse(s),\n        Ok(_) | Err(url::ParseError::RelativeUrlWithoutBase)\n    )\n}","tryCatchPattern":"match url::Url::parse(&args.page_service_connstring) {\n    Ok(url) => url.scheme().to_lowercase(),\n    Err(url::ParseError::RelativeUrlWithoutBase) => \"postgresql\".to_string(),\n    Err(err) => {\n        eprintln!(\"fix the --page-service-connstring value: {err}\");\n        return Err(anyhow!(\"invalid connstring: {err}\"));\n    }\n}","preventionTips":["Always pass the scheme explicitly (postgresql:// or grpc://)","Echo the resolved connstring in scripts before invoking pagebench","Reuse the exact page_service URL from the pageserver config instead of retyping it","Add a CI smoke test that Url::parse's stored connstrings"],"tags":["pagebench","connstring","url-parsing","cli","configuration"],"backgroundTag":"invalid-connection-string","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}