{"record":{"id":"bfde5bed3ceb3891","repo":"uber-go/zap","slug":"must-start-with-a-letter","errorCode":null,"errorMessage":"must start with a letter","messagePattern":"must start with a letter","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sink.go","lineNumber":165,"sourceCode":"\n\treturn sr.newFileSinkFromPath(u.Path)\n}\n\nfunc (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) {\n\tswitch path {\n\tcase \"stdout\":\n\t\treturn nopCloserSink{os.Stdout}, nil\n\tcase \"stderr\":\n\t\treturn nopCloserSink{os.Stderr}, nil\n\t}\n\treturn sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)\n}\n\nfunc normalizeScheme(s string) (string, error) {\n\t// https://tools.ietf.org/html/rfc3986#section-3.1\n\ts = strings.ToLower(s)\n\tif first := s[0]; 'a' > first || 'z' < first {\n\t\treturn \"\", errors.New(\"must start with a letter\")\n\t}\n\tfor i := 1; i < len(s); i++ { // iterate over bytes, not runes\n\t\tc := s[i]\n\t\tswitch {\n\t\tcase 'a' <= c && c <= 'z':\n\t\t\tcontinue\n\t\tcase '0' <= c && c <= '9':\n\t\t\tcontinue\n\t\tcase c == '.' || c == '+' || c == '-':\n\t\t\tcontinue\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"may not contain %q\", c)\n\t}\n\treturn s, nil\n}\n","sourceCodeStart":147,"sourceCodeEnd":181,"githubUrl":"https://github.com/uber-go/zap/blob/bbd4ecbd8760678fdbac94c93d9aca8069c83b2f/sink.go#L147-L181","documentation":"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.","triggerScenarios":"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\".","commonSituations":"Malformed connection strings/DSNs; typo'd or machine-generated scheme names; schemes containing leading digits or symbols from templating errors.","solutions":["Rename the scheme to start with a letter, e.g. \"custom\" or \"s3custom\"","Sanitize/validate the scheme with a regex like ^[a-z][a-z0-9+.-]*$ before use"],"exampleFix":"// before\nzap.RegisterSink(\"1custom\", factory)\n// after\nzap.RegisterSink(\"custom1\", factory)","handlingStrategy":"validation","validationCode":"var schemeRe = regexp.MustCompile(`^[a-z][a-z0-9+.-]*$`)\nif !schemeRe.MatchString(strings.ToLower(scheme)) {\n    return fmt.Errorf(\"invalid scheme %q\", scheme)\n}\nerr := zap.RegisterSink(scheme, factory)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure schemes start with an ASCII letter","Validate against RFC 3986 scheme regex before registering or building sink URLs","Watch for templating errors injecting digits/punctuation into scheme positions"],"tags":["sink","url","validation","zap","go"],"backgroundTag":"invalid-url-scheme","analyzedSha":"bbd4ecbd8760678fdbac94c93d9aca8069c83b2f","analyzedAt":"2026-08-31T13:01:40.228Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}