{"record":{"id":"5b82f281f5e4e867","repo":"uber-go/zap","slug":"can-t-register-a-sink-factory-for-empty-string","errorCode":null,"errorMessage":"can't register a sink factory for empty string","messagePattern":"can't register a sink factory for empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sink.go","lineNumber":80,"sourceCode":"}\n\nfunc newSinkRegistry() *sinkRegistry {\n\tsr := &sinkRegistry{\n\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) {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/uber-go/zap/blob/bbd4ecbd8760678fdbac94c93d9aca8069c83b2f/sink.go#L62-L98","documentation":"sinkRegistry.RegisterSink refuses to register a sink factory under an empty scheme, since schemes are the lookup key for sink URLs and an empty key is unusable/ambiguous. It normalizes the scheme and stores the factory for future newSink lookups.","triggerScenarios":"Calling zap.RegisterSink(\"\", factory) or a registry's RegisterSink with scheme == \"\".","commonSituations":"Programmatic registration where the scheme comes from parsing a URL or config value that is empty (e.g. url.Parse of a relative URL yields Scheme \"\").","solutions":["Pass a real scheme, e.g. zap.RegisterSink(\"custom\", factory)","Validate/derive the scheme from the URL before registering: u.Scheme"],"exampleFix":"// before\nerr := zap.RegisterSink(u.Scheme, factory) // u.Scheme == \"\"\n// after\nif u.Scheme == \"\" { return errors.New(\"scheme required\") }\nerr := zap.RegisterSink(u.Scheme, factory)","handlingStrategy":"validation","validationCode":"if scheme == \"\" {\n    return errors.New(\"sink scheme required\")\n}\nerr := zap.RegisterSink(scheme, factory)","typeGuard":null,"tryCatchPattern":"if err := zap.RegisterSink(scheme, factory); err != nil {\n    return fmt.Errorf(\"register sink %q: %w\", scheme, err)\n}","preventionTips":["Derive scheme from url.Parse(...).Scheme and check non-empty","Register sinks in package init with literal scheme strings"],"tags":["sink","registration","zap","go"],"backgroundTag":"empty-required-argument","analyzedSha":"bbd4ecbd8760678fdbac94c93d9aca8069c83b2f","analyzedAt":"2026-08-31T13:01:40.228Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}