{"record":{"id":"34ee678772340906","repo":"neondatabase/neon","slug":"invalid-shard-url-url-must-use-grpc","errorCode":null,"errorMessage":"invalid shard URL {url}: must use gRPC","messagePattern":"invalid shard URL (.+?): must use gRPC","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pageserver/client_grpc/src/client.rs","lineNumber":424,"sourceCode":"            return Err(anyhow!(\"stripe size can't be given for unsharded tenants\"));\n        }\n\n        // Validate the shard spec.\n        for (shard_id, url) in &urls {\n            // The shard index must match the computed shard count, even for unsharded tenants.\n            if shard_id.shard_count != count {\n                return Err(anyhow!(\"invalid shard index {shard_id}, expected {count}\"));\n            }\n            // The shard index' number and count must be consistent.\n            if !shard_id.is_unsharded() && shard_id.shard_number.0 >= shard_id.shard_count.0 {\n                return Err(anyhow!(\"invalid shard index {shard_id}\"));\n            }\n            // The above conditions guarantee that we have all shards 0..count: len() matches count,\n            // shard number < count, and numbers are unique (via hashmap).\n\n            // Validate the URL.\n            if PageserverProtocol::from_connstring(url)? != PageserverProtocol::Grpc {\n                return Err(anyhow!(\"invalid shard URL {url}: must use gRPC\"));\n            }\n        }\n\n        Ok(Self {\n            urls,\n            count,\n            stripe_size,\n        })\n    }\n}\n\n/// Tracks the tenant's shards.\nstruct Shards {\n    /// Shards by shard index.\n    ///\n    /// INVARIANT: every shard 0..count is present.\n    /// INVARIANT: shard 0 is always present.\n    by_index: HashMap<ShardIndex, Shard>,","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/client_grpc/src/client.rs#L406-L442","documentation":"The gRPC pageserver client only talks to pageservers over gRPC. Each URL is checked with PageserverProtocol::from_connstring (libs/compute_api/src/spec.rs), which maps postgresql:// and postgres:// to Libpq and, importantly, defaults to Libpq when the string has no URL scheme at all. Any URL that does not resolve to the grpc:// scheme is rejected.","triggerScenarios":"ShardSpec::new with a connstring like postgresql://host:6400, or a bare host:port with no scheme (parses as relative URL, defaults to Libpq). Only grpc://host:port passes the check.","commonSituations":"Reusing libpq pageserver connstrings from older configurations; environment variables or specs that omit the scheme; documentation examples that predate the gRPC protocol.","solutions":["Prefix every shard URL with grpc://, for example grpc://pageserver-0.ps.svc:7000.","Search the spec or config for postgres://, postgresql://, and scheme-less host:port entries and rewrite them.","If from_connstring itself failed (\"invalid connstring URL\"), fix the URL syntax first; that error propagates before the protocol comparison."],"exampleFix":"// before\nlet urls = HashMap::from([(idx, \"postgresql://ps-0:6400\".to_string())]);\nlet spec = ShardSpec::new(urls, stripe)?; // -> \"invalid shard URL postgresql://ps-0:6400: must use gRPC\"\n\n// after\nlet urls = HashMap::from([(idx, \"grpc://ps-0:7000\".to_string())]);\nlet spec = ShardSpec::new(urls, stripe)?;","handlingStrategy":"validation","validationCode":"// all URLs must resolve to the grpc protocol before building the spec\nfor url in urls.values() {\n    anyhow::ensure!(\n        PageserverProtocol::from_connstring(url)? == PageserverProtocol::Grpc,\n        \"URL {url} must use the grpc:// scheme\"\n    );\n}","typeGuard":"fn is_grpc_connstring(url: &str) -> bool {\n    url.trim_start_matches(\"grpc://\").len() != url.len()\n}","tryCatchPattern":null,"preventionTips":["Default connstrings to the grpc:// scheme at the config layer for gRPC clients.","Lint specs for postgres://, postgresql://, and scheme-less host:port entries before deployment.","Remember from_connstring defaults a scheme-less string to Libpq, not to an error."],"tags":["rust","neon","pageserver","grpc","url-scheme"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}