neondatabase/neon · error
invalid connstring: {err}
Error message
invalid connstring: {err} What it means
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.
Source
Thrown at pageserver/pagebench/src/cmd/basebackup.rs:162
loop {
let start = std::time::Instant::now();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let completed_requests = stats.completed_requests.swap(0, Ordering::Relaxed);
let elapsed = start.elapsed();
info!(
"RPS: {:.0}",
completed_requests as f64 / elapsed.as_secs_f64()
);
}
}
});
let mut work_senders = HashMap::new();
let mut tasks = Vec::new();
let scheme = match Url::parse(&args.page_service_connstring) {
Ok(url) => url.scheme().to_lowercase().to_string(),
Err(url::ParseError::RelativeUrlWithoutBase) => "postgresql".to_string(),
Err(err) => return Err(anyhow!("invalid connstring: {err}")),
};
for &tl in &timelines {
let (sender, receiver) = tokio::sync::mpsc::channel(1); // TODO: not sure what the implications of this are
work_senders.insert(tl, sender);
let client: Box<dyn Client> = match scheme.as_str() {
"postgresql" | "postgres" => Box::new(
LibpqClient::new(&args.page_service_connstring, tl, !args.no_compression).await?,
),
"grpc" => Box::new(
GrpcClient::new(&args.page_service_connstring, tl, !args.no_compression).await?,
),
scheme => return Err(anyhow!("invalid scheme {scheme}")),
};
tasks.push(tokio::spawn(run_worker(
client,
Arc::clone(&start_work_barrier),View on GitHub (pinned to 8f60b04da4)
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
Example fix
# before pagebench basebackup --page-service-connstring 'postgres://localhost:notaport' # after pagebench basebackup --page-service-connstring 'postgres://localhost:6400'
Defensive patterns
Strategy: validation
Validate before calling
fn validate_connstring(s: &str) -> anyhow::Result<String> {
match url::Url::parse(s) {
Ok(u) => Ok(u.scheme().to_lowercase()),
Err(url::ParseError::RelativeUrlWithoutBase) => Ok("postgresql".to_string()),
Err(e) => anyhow::bail!("invalid connstring: {e}"),
}
}
// run before starting the bench
let scheme = validate_connstring(&args.page_service_connstring)?; Type guard
fn is_parseable_connstring(s: &str) -> bool {
matches!(
url::Url::parse(s),
Ok(_) | Err(url::ParseError::RelativeUrlWithoutBase)
)
} Try / catch
match url::Url::parse(&args.page_service_connstring) {
Ok(url) => url.scheme().to_lowercase(),
Err(url::ParseError::RelativeUrlWithoutBase) => "postgresql".to_string(),
Err(err) => {
eprintln!("fix the --page-service-connstring value: {err}");
return Err(anyhow!("invalid connstring: {err}"));
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- invalid scheme {scheme}
- Supply either config file path or --config=inline-config
- must specify exactly one target
- pageserver has less than limit_to_first_n_targets={limit} te
- pageserver connection information should be provided
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/b07901cef1716c24.
Report an issue: GitHub.