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

  1. Pass a real scheme, e.g. zap.RegisterSink("custom", factory)
  2. 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

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


AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31). Data as JSON: /api/errors/5b82f281f5e4e867. Report an issue: GitHub.