{"record":{"id":"27ca262c41c7fcdc","repo":"jaegertracing/jaeger","slug":"invalid-server-url-q-w","errorCode":null,"errorMessage":"invalid server URL %q: %w","messagePattern":"invalid server URL %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/elasticsearch/esclient/transport.go","lineNumber":65,"sourceCode":"\t// discoverNodes enables one-shot node discovery (sniffing) at startup.\n\tdiscoverNodes bool\n\t// logLevel selects the client log-level (debug/info/error); empty disables\n\t// client logging. logger is the destination for those logs.\n\tlogLevel string\n\tlogger   *zap.Logger\n}\n\n// newRawClient builds a rawClient that round-robins requests across servers,\n// sending each through base.\nfunc newRawClient(ctx context.Context, base http.RoundTripper, opts rawClientOptions) (*rawClient, error) {\n\tif len(opts.servers) == 0 {\n\t\treturn nil, errors.New(\"no servers specified\")\n\t}\n\turls := make([]*url.URL, 0, len(opts.servers))\n\tfor _, server := range opts.servers {\n\t\tu, err := url.Parse(server)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid server URL %q: %w\", server, err)\n\t\t}\n\t\t// url.Parse accepts host:port or bare hosts as scheme/path-only URLs; the\n\t\t// pool needs a scheme and host, so reject those up front with a clear error.\n\t\tif u.Scheme == \"\" || u.Host == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"server URL %q must include a scheme and host, e.g. http://host:9200\", server)\n\t\t}\n\t\turls = append(urls, u)\n\t}\n\t// Retry is disabled to preserve the current admin-client behavior; the data\n\t// plane can opt into the pool's read retry when it adopts rawClient in Stage B.\n\ttransportOpts := []elastictransport.Option{\n\t\telastictransport.WithURLs(urls...),\n\t\telastictransport.WithTransport(base),\n\t\telastictransport.WithDisableRetry(),\n\t}\n\tif opts.compressRequestBody {\n\t\t// Defaults to gzip.DefaultCompression, matching the level the olivere\n\t\t// client used before it was retired. The pool gzips the body and sets","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/elasticsearch/esclient/transport.go#L47-L83","documentation":"newRawClient parses each configured server address with net/url before building the connection pool. This error means url.Parse itself rejected the string (control characters, invalid percent-escapes, malformed IPv6 brackets, etc.), so the client cannot be constructed at all. It is returned from NewClient and surfaces at storage initialization time, not at request time.","triggerScenarios":"Calling NewClient (or the tests that build raw clients) with a server string net/url cannot parse — e.g. a URL containing raw spaces or control characters, invalid %-encoding like http://host:9200/%zz, or unbalanced brackets in an IPv6 literal like http://[::1:9200. Comma-separated or quoted strings leaked in from misparsed config will also fail here.","commonSituations":"Env var SPAN_STORAGE_TYPE elasticsearch config with stray whitespace/newlines or surrounding quotes pasted into the servers list; Kubernetes env interpolation introducing control characters; hand-edited YAML producing 'http://es:9200,http://es2:9200' as a single element; shell quoting stripping characters like [ or ] from IPv6 addresses.","solutions":["Print the exact server string from the error message and inspect it for stray characters (spaces, quotes, newlines, commas).","Fix the URL to a valid form, e.g. http://host:9200 or http://[::1]:9200 for IPv6, and URL-escape any special characters.","If passing multiple servers, provide them as separate list elements (or split on commas) rather than one concatenated string.","If %-escapes are intended, percent-encode them correctly (e.g. %25 for a literal %)."],"exampleFix":"// before\nservers: [\"http://es-1:9200,http://es-2:9200\"]\n// after\nservers:\n  - http://es-1:9200\n  - http://es-2:9200","handlingStrategy":"validation","validationCode":"for _, s := range servers {\n    u, err := url.Parse(strings.TrimSpace(s))\n    if err != nil {\n        return fmt.Errorf(\"invalid server URL %q: %w\", s, err)\n    }\n    if u.Scheme == \"\" || u.Host == \"\" {\n        return fmt.Errorf(\"server URL %q must include scheme and host\", s)\n    }\n}","typeGuard":"func isValidServerURL(s string) bool {\n    u, err := url.Parse(strings.TrimSpace(s))\n    return err == nil && u.Scheme != \"\" && u.Host != \"\"\n}","tryCatchPattern":"client, err := esclient.NewClient(...)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid server URL\") {\n        log.Fatalf(\"bad ES server config: %v\", err) // fail fast at startup, not at request time\n    }\n    return err\n}","preventionTips":["Validate every server URL with url.Parse (plus scheme/host check) in config parsing, before the client is constructed.","Trim whitespace and strip surrounding quotes from env-var/YAML config values.","Pass multiple servers as separate list elements, never as one comma-joined string.","Use bracketed IPv6 form http://[::1]:9200 and percent-encode special characters.","Fail fast: surface the raw string from the error message when logging the config at startup."],"tags":["configuration","url-parsing","elasticsearch","startup"],"backgroundTag":"invalid-url","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}