uber-go/zap · error
can't register a sink factory for empty string
Error message
can't register a sink factory for empty string
What it means
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.
Source
Thrown at sink.go:80
}
func newSinkRegistry() *sinkRegistry {
sr := &sinkRegistry{
factories: make(map[string]func(*url.URL) (Sink, error)),
openFile: os.OpenFile,
}
// Infallible operation: the registry is empty, so we can't have a conflict.
_ = sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)
return sr
}
// RegisterSink registers the given factory for the specific scheme.
func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
sr.mu.Lock()
defer sr.mu.Unlock()
if scheme == "" {
return errors.New("can't register a sink factory for empty string")
}
normalized, err := normalizeScheme(scheme)
if err != nil {
return fmt.Errorf("%q is not a valid scheme: %v", scheme, err)
}
if _, ok := sr.factories[normalized]; ok {
return fmt.Errorf("sink factory already registered for scheme %q", normalized)
}
sr.factories[normalized] = factory
return nil
}
func (sr *sinkRegistry) newSink(rawURL string) (Sink, error) {
// URL parsing doesn't work well for Windows paths such as `c:\log.txt`, as scheme is set to
// the drive, and path is unset unless `c:/log.txt` is used.
// To avoid Windows-specific URL handling, we instead check IsAbs to open as a file.
// filepath.IsAbs is OS-specific, so IsAbs('c:/log.txt') is false outside of Windows.
if filepath.IsAbs(rawURL) {View on GitHub (pinned to bbd4ecbd87)
Solutions
- Pass a real scheme, e.g. zap.RegisterSink("custom", factory)
- Validate/derive the scheme from the URL before registering: u.Scheme
Example fix
// before
err := zap.RegisterSink(u.Scheme, factory) // u.Scheme == ""
// after
if u.Scheme == "" { return errors.New("scheme required") }
err := zap.RegisterSink(u.Scheme, factory) Defensive patterns
Strategy: validation
Validate before calling
if scheme == "" {
return errors.New("sink scheme required")
}
err := zap.RegisterSink(scheme, factory) Try / catch
if err := zap.RegisterSink(scheme, factory); err != nil {
return fmt.Errorf("register sink %q: %w", scheme, err)
} Prevention
- Derive scheme from url.Parse(...).Scheme and check non-empty
- Register sinks in package init with literal scheme strings
When it happens
Trigger: Calling zap.RegisterSink("", factory) or a registry's RegisterSink with scheme == "".
Common situations: 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 "").
Related errors
- no encoder name specified
- must start with a letter
- encoder already registered for name %q
- no encoder registered for name %q
- missing Level
AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31).
Data as JSON: /api/errors/5b82f281f5e4e867.
Report an issue: GitHub.