{"record":{"id":"815fb48b3164c625","repo":"caddyserver/caddy","slug":"s-entry-q-does-not-contain-a-q","errorCode":null,"errorMessage":"%s: entry %q does not contain a %q","messagePattern":"(.+?): entry %q does not contain a %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/server.go","lineNumber":411,"sourceCode":"\t\t\tif entry[i] >= 0x80 {\n\t\t\t\treturn nil, nil, nil, fmt.Errorf(\"%s: entry %q contains non-ASCII characters\", directive, entry)\n\t\t\t}\n\t\t}\n\n\t\tisGlob := strings.HasSuffix(entry, \"*\")\n\t\tname := entry\n\t\tif isGlob {\n\t\t\tname = strings.TrimSuffix(entry, \"*\")\n\t\t}\n\n\t\t// Reject entries with '*' not at the trailing position.\n\t\tif strings.ContainsRune(name, '*') {\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"%s: entry %q has '*' in an invalid position (only a trailing '*' is allowed)\", directive, entry)\n\t\t}\n\n\t\t// The name (without trailing '*') must contain at least one separator.\n\t\tif !strings.ContainsRune(name, sep) {\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"%s: entry %q does not contain a %q\", directive, entry, sep)\n\t\t}\n\n\t\tcanonAllow := http.CanonicalHeaderKey(name)\n\t\tcanonDrop := http.CanonicalHeaderKey(strings.ReplaceAll(name, string(sep), \"-\"))\n\n\t\tif isGlob {\n\t\t\tprefixRules = append(prefixRules, aliasPrefixRule{\n\t\t\t\tallow: canonAllow,\n\t\t\t\tdrop:  canonDrop,\n\t\t\t})\n\t\t} else {\n\t\t\texactAllow[canonAllow] = struct{}{}\n\t\t\texactDrop[canonDrop] = struct{}{}\n\t\t}\n\t}\n\n\treturn exactAllow, exactDrop, prefixRules, nil\n}","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/server.go#L393-L429","documentation":"Each header alias allowlist entry (after removing a trailing '*') must contain at least one separator character — '_' or '.', depending on the directive — because the alias mechanism works by rewriting that separator to/from '-' in header names. Entries without a separator are rejected at provisioning with the required separator shown via %q.","triggerScenarios":"An entry like \"XApiKey\" or \"xapi\" with no '_' or '.'; only names such as \"X_Api_Key\" or \"x.api.key\" are meaningful aliases.","commonSituations":"Listing ordinary header names (no separator) in the alias allowlist, misunderstanding that the feature targets underscore/dot-to-dash canonicalization.","solutions":["Add the required separator to the header name so an alias actually exists","Remove entries that name headers without separators — they cannot be aliased","Check the %q in the message to confirm which separator ('_' or '.') the directive expects"],"exampleFix":"// before\n\"-\": [\"XApiKey\"]\n\n// after\n\"-\": [\"X_Api_Key\"]","handlingStrategy":"validation","validationCode":"func hasSeparator(entries []string, sep rune) error {\n    for _, e := range entries {\n        name := strings.TrimSuffix(e, \"*\")\n        if !strings.ContainsRune(name, sep) {\n            return fmt.Errorf(\"entry %q lacks required %q separator\", e, sep)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Understand the feature aliases '_'/'.' to '-', so only names with separators belong in the list","Read the directive's %q output to confirm which separator is required"],"tags":["caddy","headers","config-validation","separator"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}