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

  1. Log/inspect the exact connstring and the embedded {err} from url::ParseError (EmptyHost is the most common) and fix the host part
  2. If you meant a scheme-less connstring, remove the scheme separator entirely so parse hits RelativeUrlWithoutBase and defaults to Libpq
  3. Validate connstrings at config load time with Url::parse before they reach from_connstring
  4. 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

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


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/d5c78f0eb0cdb49c. Report an issue: GitHub.