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

  1. Pass a plain scheme identifier like 'custom' or 'redis' (no '://', no spaces).
  2. Trim whitespace and validate the scheme with a regex like ^[a-zA-Z][a-zA-Z0-9+.-]*$ before registering.
  3. 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

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


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