neondatabase/neon · error
invalid connstring URL: {err}
Error message
invalid connstring URL: {err} What it means
Thrown by PageserverProtocol::from_connstring() in neon's compute_api when the `url` crate fails to parse the connstring with any error other than RelativeUrlWithoutBase (which is treated as a scheme-less connstring and defaults to Libpq). Typical underlying url::ParseError values are EmptyHost and invalid characters/ports, so this fires on strings that look like a URL (contain a scheme separator) but are malformed as URLs.
Source
Thrown at libs/compute_api/src/spec.rs:648
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub enum PageserverProtocol {
/// The original protocol based on libpq and COPY. Uses postgresql:// or postgres:// scheme.
#[default]
#[serde(rename = "libpq")]
Libpq,
/// A newer, gRPC-based protocol. Uses grpc:// scheme.
#[serde(rename = "grpc")]
Grpc,
}
impl PageserverProtocol {
/// Parses the protocol from a connstring scheme. Defaults to Libpq if no scheme is given.
/// Errors if the connstring is an invalid URL.
pub fn from_connstring(connstring: &str) -> anyhow::Result<Self> {
let scheme = match Url::parse(connstring) {
Ok(url) => url.scheme().to_lowercase(),
Err(url::ParseError::RelativeUrlWithoutBase) => return Ok(Self::default()),
Err(err) => return Err(anyhow!("invalid connstring URL: {err}")),
};
match scheme.as_str() {
"postgresql" | "postgres" => Ok(Self::Libpq),
"grpc" => Ok(Self::Grpc),
scheme => Err(anyhow!("invalid protocol scheme: {scheme}")),
}
}
/// Returns the URL scheme for the protocol, for use in connstrings.
pub fn scheme(&self) -> &'static str {
match self {
Self::Libpq => "postgresql",
Self::Grpc => "grpc",
}
}
}
impl Display for PageserverProtocol {View on GitHub (pinned to 8f60b04da4)
Solutions
- Log/inspect the exact connstring and the embedded {err} from url::ParseError (EmptyHost is the most common) and fix the host part
- If you meant a scheme-less connstring, remove the scheme separator entirely so parse hits RelativeUrlWithoutBase and defaults to Libpq
- Validate connstrings at config load time with Url::parse before they reach from_connstring
- Check for empty variables used to build the URL (empty host, empty port)
Example fix
# before PAGESERVER_CONNSTRING="postgres://" # EmptyHost -> error PAGESERVER_CONNSTRING="grpc://:50051" # EmptyHost -> error # after PAGESERVER_CONNSTRING="postgres://pageserver-0.neon:6400" PAGESERVER_CONNSTRING="grpc://pageserver-0.neon:50051"
Defensive patterns
Strategy: validation
Validate before calling
// Validate connstrings at config load time:
fn valid_connstring(s: &str) -> bool {
match url::Url::parse(s) {
Ok(_) => true,
Err(url::ParseError::RelativeUrlWithoutBase) => true, // scheme-less is OK
Err(_) => false,
}
} Try / catch
// Log the underlying url::ParseError and the offending string at startup:
if let Err(e) = PageserverProtocol::from_connstring(&cfg.ps_connstring) {
anyhow::bail!("bad PAGESERVER_CONNSTRING {:?}: {e}", cfg.ps_connstring);
} Prevention
- Never build connstrings by naive concatenation of possibly-empty variables
- Validate connstrings when loading config, not at first use deep in startup
- Prefer fully-qualified URLs (scheme + host) in configuration files
When it happens
Trigger: Calling from_connstring("postgres://") or from_connstring("grpc://:50051") (EmptyHost), or a connstring with illegal characters in the host/port such as "postgresql://host:notaport/db". Any scheme-bearing string that Url::parse rejects triggers it.
Common situations: Config or environment variable holding a half-filled connstring, e.g. postgres:// with the host templated out; connstrings assembled by string concatenation where a variable expanded to empty; copy-paste of a libpq keyword/value string (host=localhost port=5432) into a field that expects a URL; trailing spaces or control characters.
Related errors
- no grpc_url for shard {shard_index}
- no libpq_url for shard {shard_index}
- invalid protocol scheme: {scheme}
- empty connection string
- must have at least one pageserver
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/d5c78f0eb0cdb49c.
Report an issue: GitHub.