{"record":{"id":"c51a925402ba8a09","repo":"cloudflare/cloudflared","slug":"allowed-mail-rule-d-is-empty","errorCode":null,"errorMessage":"allowed mail rule %d is empty","messagePattern":"allowed mail rule (.+?) is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"connection/quick_tunnel_auth_validation.go","lineNumber":21,"sourceCode":"import (\n\t\"fmt\"\n\t\"net/mail\"\n\t\"strings\"\n\n\t\"golang.org/x/net/idna\"\n)\n\n// validateQuickTunnelAllowedMail validates and normalizes exact email addresses and\n// wildcard domains from one or more comma-separated values.\nfunc validateQuickTunnelAllowedMail(values []string) (emails, wildcardDomains map[string]struct{}, err error) {\n\temails, wildcardDomains = make(map[string]struct{}), make(map[string]struct{})\n\tfor i, rawEntry := range strings.Split(strings.Join(values, \",\"), \",\") {\n\t\tentry := normalizeQuickTunnelEmail(rawEntry)\n\t\tdomain, isWildcard := strings.CutPrefix(entry, \"*@\")\n\n\t\tswitch {\n\t\tcase entry == \"\":\n\t\t\treturn nil, nil, fmt.Errorf(\"allowed mail rule %d is empty\", i+1)\n\n\t\tcase isWildcard:\n\t\t\tif !isValidQuickTunnelEmailDomain(domain) {\n\t\t\t\treturn nil, nil, fmt.Errorf(\n\t\t\t\t\t\"allowed mail rule %q has an invalid wildcard domain\",\n\t\t\t\t\trawEntry,\n\t\t\t\t)\n\t\t\t}\n\t\t\twildcardDomains[domain] = struct{}{}\n\n\t\tdefault:\n\t\t\tif !isValidQuickTunnelEmail(entry) {\n\t\t\t\treturn nil, nil, fmt.Errorf(\n\t\t\t\t\t\"allowed mail rule %q is not a valid email address\",\n\t\t\t\t\trawEntry,\n\t\t\t\t)\n\t\t\t}\n\t\t\temails[entry] = struct{}{}","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/connection/quick_tunnel_auth_validation.go#L3-L39","documentation":"validateQuickTunnelAllowedMail parses the allowed-mail configuration into individual rules (splitting on commas after joining values). An empty entry between or around commas yields \"allowed mail rule %d is empty\". The validator fails fast so an empty allowlist rule doesn't accidentally broaden or corrupt quick-tunnel email access validation.","triggerScenarios":"The allowed mail configuration contains consecutive commas (e.g. \"a@x.com,,b@x.com\"), a leading/trailing comma (\",a@x.com\"), or whitespace-only entries that normalize to empty strings.","commonSituations":"Hand-edited config files with stray commas; env-var list values with trailing commas; merge artifacts in YAML/JSON config where an array element became empty.","solutions":["Remove empty entries and stray commas from the allowed mail list","Trim whitespace in the config so entries don't normalize to empty","Validate the config value with a quick split-and-check before applying it","If generated programmatically, filter out empty strings before joining with commas"],"exampleFix":"// before\nallowedMail: \"alice@example.com,,bob@example.com\"\n// after\nallowedMail: \"alice@example.com,bob@example.com\"","handlingStrategy":"validation","validationCode":"entries := strings.Split(config.AllowedMail, \",\")\nfor i, e := range entries {\n\tif strings.TrimSpace(e) == \"\" {\n\t\treturn fmt.Errorf(\"allowed mail rule %d is empty\", i+1)\n\t}\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Trim each entry and drop empties before joining with commas","Avoid hand-editing comma lists; use structured YAML/JSON arrays","Lint config files for trailing/duplicate commas"],"tags":["go","config","validation","email"],"backgroundTag":"empty-required-field","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}