uber-go/zap · error

must start with a letter

Error message

must start with a letter

What it means

Per RFC 3986 §3.1, a URI scheme must begin with an ASCII letter. normalizeScheme lowercases the input and returns errors.New("must start with a letter") when the first byte is outside a-z, before validating the remaining characters.

Source

Thrown at sink.go:165

	return sr.newFileSinkFromPath(u.Path)
}

func (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) {
	switch path {
	case "stdout":
		return nopCloserSink{os.Stdout}, nil
	case "stderr":
		return nopCloserSink{os.Stderr}, nil
	}
	return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)
}

func normalizeScheme(s string) (string, error) {
	// https://tools.ietf.org/html/rfc3986#section-3.1
	s = strings.ToLower(s)
	if first := s[0]; 'a' > first || 'z' < first {
		return "", errors.New("must start with a letter")
	}
	for i := 1; i < len(s); i++ { // iterate over bytes, not runes
		c := s[i]
		switch {
		case 'a' <= c && c <= 'z':
			continue
		case '0' <= c && c <= '9':
			continue
		case c == '.' || c == '+' || c == '-':
			continue
		}
		return "", fmt.Errorf("may not contain %q", c)
	}
	return s, nil
}

View on GitHub (pinned to bbd4ecbd87)

Solutions

  1. Rename the scheme to start with a letter, e.g. "custom" or "s3custom"
  2. Sanitize/validate the scheme with a regex like ^[a-z][a-z0-9+.-]*$ before use

Example fix

// before
zap.RegisterSink("1custom", factory)
// after
zap.RegisterSink("custom1", factory)
Defensive patterns

Strategy: validation

Validate before calling

var schemeRe = regexp.MustCompile(`^[a-z][a-z0-9+.-]*$`)
if !schemeRe.MatchString(strings.ToLower(scheme)) {
    return fmt.Errorf("invalid scheme %q", scheme)
}
err := zap.RegisterSink(scheme, factory)

Prevention

When it happens

Trigger: Registering or opening a sink whose scheme starts with a non-letter character, e.g. RegisterSink("1custom", f) or opening a URL like "123://path".

Common situations: Malformed connection strings/DSNs; typo'd or machine-generated scheme names; schemes containing leading digits or symbols from templating errors.

Related errors


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