{"record":{"id":"e9e91566385ff21e","repo":"neondatabase/neon","slug":"invalid-scheme-scheme","errorCode":null,"errorMessage":"invalid scheme {scheme}","messagePattern":"invalid scheme (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pageserver/pagebench/src/cmd/basebackup.rs","lineNumber":175,"sourceCode":"    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),\n            receiver,\n            Arc::clone(&all_work_done_barrier),\n            Arc::clone(&live_stats),\n        )));\n    }\n\n    let work_sender = async move {\n        start_work_barrier.wait().await;\n        loop {\n            let (timeline, work) = {\n                let mut rng = rand::rng();\n                let target = all_targets.choose(&mut rng).unwrap();\n                let lsn = target.lsn_range.clone().map(|r| rng.random_range(r));","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/pagebench/src/cmd/basebackup.rs#L157-L193","documentation":"pagebench basebackup accepts exactly three scheme values after lowercasing: postgresql and postgres (LibpqClient over the page protocol) and grpc (GrpcClient). Any other scheme reaches the catch-all match arm and returns 'invalid scheme {scheme}'. A common trap: 'localhost:6400' without a prefix parses successfully with 'localhost' as the scheme, producing this error rather than a connstring parse error.","triggerScenarios":"Passing a bare host:port whose host name is a valid scheme token (localhost:6400, myhost:7000); using http://, https://, or tcp:// URLs copied from the management API; scheme typos such as postgressql:// or pg://.","commonSituations":"Forgetting the postgresql:// prefix; copy-pasting the HTTP management endpoint instead of the page_service endpoint; trying the experimental gRPC transport before enabling its listener on the pageserver.","solutions":["Prefix the target with postgresql:// (or postgres://), e.g. postgresql://postgres@localhost:6400","Use grpc://host:port only when the pageserver's gRPC page_api listener is enabled and reachable","Check the scheme spelling against the three accepted values"],"exampleFix":"# before\npagebench basebackup --page-service-connstring 'localhost:6400'   # scheme parses as 'localhost'\n# after\npagebench basebackup --page-service-connstring 'postgresql://postgres@localhost:6400'","handlingStrategy":"validation","validationCode":"const SUPPORTED: [&str; 3] = [\"postgresql\", \"postgres\", \"grpc\"];\n\nfn resolve_scheme(connstring: &str) -> Option<String> {\n    let scheme = match url::Url::parse(connstring) {\n        Ok(u) => u.scheme().to_lowercase(),\n        Err(url::ParseError::RelativeUrlWithoutBase) => \"postgresql\".to_string(),\n        Err(_) => return None,\n    };\n    SUPPORTED.contains(&scheme.as_str()).then_some(scheme)\n}","typeGuard":"fn is_supported_scheme(connstring: &str) -> bool {\n    resolve_scheme(connstring).is_some()\n}","tryCatchPattern":"match scheme.as_str() {\n    \"postgresql\" | \"postgres\" => build_libpq_client().await,\n    \"grpc\" => build_grpc_client().await,\n    other => {\n        eprintln!(\"scheme '{other}' not supported; use postgresql:// or grpc://\");\n        Err(anyhow!(\"invalid scheme {other}\"))\n    }\n}","preventionTips":["Never pass a bare host:port; always include the scheme","Keep page_service and management-API URLs in separate, clearly named variables","Fail fast on scheme validation before spawning worker tasks"],"tags":["pagebench","url-scheme","cli","configuration"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}