uber-go/zap · error
%q is not a valid scheme: %v
Error message
%q is not a valid scheme: %v
What it means
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.
Source
Thrown at sink.go:84
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) {
return sr.newFileSinkFromPath(rawURL)
}
u, err := url.Parse(rawURL)View on GitHub (pinned to bbd4ecbd87)
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.
Example fix
// before
err := zap.RegisterSink(os.Getenv("LOG_SCHEME"), factory) // empty env -> error
// after
scheme := strings.TrimSpace(os.Getenv("LOG_SCHEME"))
if scheme == "" {
scheme = "file"
}
err := zap.RegisterSink(scheme, factory) Defensive patterns
Strategy: validation
Validate before calling
var schemeRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9+.-]*$`)
func validScheme(s string) bool { return s != "" && schemeRe.MatchString(strings.TrimSpace(s)) } Try / catch
if err := zap.RegisterSink(scheme, factory); err != nil {
return fmt.Errorf("registering sink scheme %q: %w", scheme, err)
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- must start with a letter
- can't register a sink factory for empty string
- sink factory already registered for scheme %q
- can't parse %q as a URL: %v
- user and password not allowed with file URLs: got %v
AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31).
Data as JSON: /api/errors/94e25fea19a23b9f.
Report an issue: GitHub.