{"record":{"id":"5c5b52d5fc27a76f","repo":"alibaba/open-code-review","slug":"invalid-extra-header-q-expected-key-value","errorCode":null,"errorMessage":"invalid extra header %q: expected key=value","messagePattern":"invalid extra header %q: expected key=value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/llm/resolver.go","lineNumber":869,"sourceCode":"func ParseExtraHeaders(raw string) (map[string]string, error) {\n\tif raw == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tpairs, err := splitHeaderPairs(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresult := make(map[string]string)\n\tfor _, pair := range pairs {\n\t\tpair = strings.TrimSpace(pair)\n\t\tif pair == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.SplitN(pair, \"=\", 2)\n\t\tif len(parts) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"invalid extra header %q: expected key=value\", pair)\n\t\t}\n\t\tkey := strings.TrimSpace(parts[0])\n\t\tvalue := strings.TrimSpace(parts[1])\n\t\tif key == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"invalid extra header %q: empty header name\", pair)\n\t\t}\n\t\tif reservedHeaders[strings.ToLower(key)] {\n\t\t\treturn nil, fmt.Errorf(\"extra header %q conflicts with a reserved header; use the dedicated config field instead\", key)\n\t\t}\n\t\tif len(value) >= 2 && value[0] == '\"' && value[len(value)-1] == '\"' {\n\t\t\tvalue = value[1 : len(value)-1]\n\t\t}\n\t\tresult[key] = value\n\t}\n\treturn result, nil\n}\n\n// splitHeaderPairs splits a comma-separated string while respecting double-quoted segments.","sourceCodeStart":851,"sourceCodeEnd":887,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/llm/resolver.go#L851-L887","documentation":"Extra headers are configured as a list of \"key=value\" strings. During resolution, each entry is split on the first '='; an entry without a '=' separator fails with 'invalid extra header %q: expected key=value'. This gives an early, clear error instead of a silently dropped or mangled header at request time.","triggerScenarios":"An extra_headers list entry with no '=' character, e.g. extra_headers = [\"X-Custom-Trace\"] or an empty-ish token after trimming, passed through resolver validation.","commonSituations":"Adding a bare header name intending to set it later; YAML/TOML list item quoting mistakes that split 'Key: value' into the wrong field; copy-pasting an HTTP header line using ':' instead of '='.","solutions":["Rewrite the entry as key=value, e.g. extra_headers = [\"X-Custom-Trace=abc123\"]","If the header needs no value, use an empty value explicitly: \"X-Custom-Trace=\"","Check the separator: HTTP colon syntax (\"Key: value\") is not accepted here — only '='","Trim stray whitespace/quotes so the entry isn't split unexpectedly"],"exampleFix":"// before (config file)\nextra_headers = [\"X-Custom-Trace\"]\n// after\nextra_headers = [\"X-Custom-Trace=abc123\"]","handlingStrategy":"validation","validationCode":"// validate extra_headers entries before running ocr\nfor _, h := range cfg.ExtraHeaders {\n    if !strings.Contains(h, \"=\") {\n        return fmt.Errorf(\"extra header %q must be key=value\", h)\n    }\n    if strings.TrimSpace(strings.SplitN(h, \"=\", 2)[0]) == \"\" {\n        return fmt.Errorf(\"extra header %q has empty key\", h)\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, _, err := llm.ResolveEndpoint(cfg, \"\"); err != nil {\n    var ehErr = \"invalid extra header\"\n    if strings.Contains(err.Error(), ehErr) {\n        // print the offending entry quoted in the error and fix the config\n    }\n}","preventionTips":["Always author entries as key=value, even when the value is empty (\"Key=\")","Use '=' not ':' — HTTP header syntax is not accepted in config","Reserve auth/content-type style headers for dedicated fields; extras are for custom metadata"],"tags":["config","validation","http-headers"],"backgroundTag":"invalid-config-value","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}