{"record":{"id":"a35044984308b4af","repo":"neondatabase/neon","slug":"error-parsing-remote-endpoint-expected-host-por","errorCode":null,"errorMessage":"Error parsing {remote_endpoint}, expected host:port, got {err:?}","messagePattern":"Error parsing (.+?), expected host:port, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compute_tools/src/rsyslog.rs","lineNumber":102,"sourceCode":"    Ok(())\n}\n\nfn parse_audit_syslog_address(\n    remote_plain_endpoint: &str,\n    remote_tls_endpoint: &str,\n) -> Result<(String, u16, String)> {\n    let tls;\n    let remote_endpoint = if !remote_tls_endpoint.is_empty() {\n        tls = \"true\".to_string();\n        remote_tls_endpoint\n    } else {\n        tls = \"false\".to_string();\n        remote_plain_endpoint\n    };\n    // Urlify the remote_endpoint, so parsing can be done with url::Url.\n    let url_str = format!(\"http://{remote_endpoint}\");\n    let url = Url::parse(&url_str).map_err(|err| {\n        anyhow!(\"Error parsing {remote_endpoint}, expected host:port, got {err:?}\")\n    })?;\n\n    let is_valid = url.scheme() == \"http\"\n        && url.path() == \"/\"\n        && url.query().is_none()\n        && url.fragment().is_none()\n        && url.username() == \"\"\n        && url.password().is_none();\n\n    if !is_valid {\n        return Err(anyhow!(\n            \"Invalid address format {remote_endpoint}, expected host:port\"\n        ));\n    }\n    let host = match url.host() {\n        Some(Host::Domain(h)) if hostname_validator::is_valid(h) => h.to_string(),\n        Some(Host::Ipv4(ip4)) => ip4.to_string(),\n        Some(Host::Ipv6(ip6)) => ip6.to_string(),","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/compute_tools/src/rsyslog.rs#L84-L120","documentation":"parse_audit_syslog_address builds the synthetic URL `http://{remote_endpoint}` from AUDIT_LOGGING_ENDPOINT / AUDIT_LOGGING_TLS_ENDPOINT and calls Url::parse; this error means even that synthetic URL failed to parse. Practically the endpoint contains characters a URL cannot hold - repeated colons ('collector.host.tld:::5555'), spaces, control characters - or is structurally broken. The {:?} payload carries the url::ParseError variant (e.g. InvalidDomainCharacter, InvalidPort).","triggerScenarios":"Url::parse of 'http://{endpoint}' fails: multiple colons ('host:::514'), illegal characters in host/port, percent-mangled values, or garbage strings from env vars.","commonSituations":"Typoed or copy-pasted audit endpoint env values; values that already include a scheme or multiple separators; CI configurations injected with quoting artifacts.","solutions":["Set the endpoint strictly as host:port, e.g. collector.host.tld:5555","For IPv6 addresses always bracket them: [7e60:82ed:...]:5555","Read the ParseError debug in the message to see which character class broke parsing","Pre-validate the env value with a host:port parser before compute start"],"exampleFix":"# before\nAUDIT_LOGGING_ENDPOINT=collector.host.tld:::5555\n\n# after\nAUDIT_LOGGING_ENDPOINT=collector.host.tld:5555","handlingStrategy":"validation","validationCode":"// reject malformed endpoints before touching rsyslog\nfn is_host_port(endpoint: &str) -> bool {\n    let Some((host, port)) = endpoint.rsplit_once(':') else { return false };\n    let host = host.trim_start_matches('[').trim_end_matches(']');\n    let host_ok = hostname_validator::is_valid(host)\n        || host.parse::<std::net::Ipv4Addr>().is_ok()\n        || host.parse::<std::net::Ipv6Addr>().is_ok();\n    host_ok && port.parse::<u16>().is_ok()\n}\n\nif !is_host_port(&remote_endpoint) {\n    bail!(\"audit endpoint must be host:port, got {remote_endpoint:?}\");\n}","typeGuard":"fn is_host_port(endpoint: &str) -> bool {\n    let Some((host, port)) = endpoint.rsplit_once(':') else { return false };\n    let host = host.trim_start_matches('[').trim_end_matches(']');\n    (hostname_validator::is_valid(host)\n        || host.parse::<std::net::Ipv4Addr>().is_ok()\n        || host.parse::<std::net::Ipv6Addr>().is_ok())\n        && port.parse::<u16>().is_ok()\n}","tryCatchPattern":"let url = match Url::parse(&format!(\"http://{remote_endpoint}\")) {\n    Ok(u) => u,\n    Err(err) => return Err(anyhow!(\"invalid endpoint {remote_endpoint}: {err:?}\")),\n};","preventionTips":["Validate endpoints with a host:port parser before compute start","Reject values containing '://', '/', '?', '#', '@', or multiple colons early","Bracket IPv6 addresses: [addr]:port"],"tags":["url-parsing","host-port","validation","rsyslog","rust"],"backgroundTag":"url-parse-error","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}