{"record":{"id":"94e25fea19a23b9f","repo":"uber-go/zap","slug":"q-is-not-a-valid-scheme-v","errorCode":null,"errorMessage":"%q is not a valid scheme: %v","messagePattern":"%q is not a valid scheme: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sink.go","lineNumber":84,"sourceCode":"\t\tfactories: make(map[string]func(*url.URL) (Sink, error)),\n\t\topenFile:  os.OpenFile,\n\t}\n\t// Infallible operation: the registry is empty, so we can't have a conflict.\n\t_ = sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)\n\treturn sr\n}\n\n// RegisterSink registers the given factory for the specific scheme.\nfunc (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {\n\tsr.mu.Lock()\n\tdefer sr.mu.Unlock()\n\n\tif scheme == \"\" {\n\t\treturn errors.New(\"can't register a sink factory for empty string\")\n\t}\n\tnormalized, err := normalizeScheme(scheme)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%q is not a valid scheme: %v\", scheme, err)\n\t}\n\tif _, ok := sr.factories[normalized]; ok {\n\t\treturn fmt.Errorf(\"sink factory already registered for scheme %q\", normalized)\n\t}\n\tsr.factories[normalized] = factory\n\treturn nil\n}\n\nfunc (sr *sinkRegistry) newSink(rawURL string) (Sink, error) {\n\t// URL parsing doesn't work well for Windows paths such as `c:\\log.txt`, as scheme is set to\n\t// the drive, and path is unset unless `c:/log.txt` is used.\n\t// To avoid Windows-specific URL handling, we instead check IsAbs to open as a file.\n\t// filepath.IsAbs is OS-specific, so IsAbs('c:/log.txt') is false outside of Windows.\n\tif filepath.IsAbs(rawURL) {\n\t\treturn sr.newFileSinkFromPath(rawURL)\n\t}\n\n\tu, err := url.Parse(rawURL)","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/uber-go/zap/blob/bbd4ecbd8760678fdbac94c93d9aca8069c83b2f/sink.go#L66-L102","documentation":"RegisterSink validates a URL scheme before storing a custom sink factory in the registry; if normalizeScheme rejects the scheme (empty or containing invalid characters), this error is returned wrapping the validation error. It means the scheme string passed to zap.RegisterSink is not a valid URL scheme.","triggerScenarios":"Calling zap.RegisterSink(scheme, factory) where scheme is empty after trimming or contains characters not allowed in URL schemes (e.g. 'my app', 'data+', or strings with spaces or uppercase mishandled).","commonSituations":"Building a scheme dynamically from config values or log destination strings; passing an environment variable that is unset or contains whitespace; typos like 'file://'-derived values passed instead of just the scheme.","solutions":["Pass a plain scheme identifier like 'custom' or 'redis' (no '://', no spaces).","Trim whitespace and validate the scheme with a regex like ^[a-zA-Z][a-zA-Z0-9+.-]*$ before registering.","Ensure the config/env value holding the scheme is set and non-empty before calling RegisterSink."],"exampleFix":"// before\nerr := zap.RegisterSink(os.Getenv(\"LOG_SCHEME\"), factory) // empty env -> error\n// after\nscheme := strings.TrimSpace(os.Getenv(\"LOG_SCHEME\"))\nif scheme == \"\" {\n    scheme = \"file\"\n}\nerr := zap.RegisterSink(scheme, factory)","handlingStrategy":"validation","validationCode":"var schemeRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9+.-]*$`)\nfunc validScheme(s string) bool { return s != \"\" && schemeRe.MatchString(strings.TrimSpace(s)) }","typeGuard":null,"tryCatchPattern":"if err := zap.RegisterSink(scheme, factory); err != nil {\n    return fmt.Errorf(\"registering sink scheme %q: %w\", scheme, err)\n}","preventionTips":["Pass scheme names without '://', spaces, or empty values","Trim and validate config/env-derived schemes before registration","Register sinks in a controlled init path, not from arbitrary config"],"tags":["zap","sink","url-scheme","validation"],"backgroundTag":"invalid-url-scheme","analyzedSha":"bbd4ecbd8760678fdbac94c93d9aca8069c83b2f","analyzedAt":"2026-08-31T13:01:40.228Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}