{"record":{"id":"6e8e008a569c8404","repo":"alibaba/open-code-review","slug":"s-w-6e8e00","errorCode":null,"errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/llm/resolver.go","lineNumber":171,"sourceCode":"// strategy resolves the endpoint. Parsed once, up front — see the call site in\n// ResolveEndpointWithOptions for why the timing matters.\ntype envOverrides struct {\n\ttimeout    time.Duration\n\thasTimeout bool\n\theaders    map[string]string\n}\n\nfunc parseEnvOverrides() (envOverrides, error) {\n\tvar env envOverrides\n\tvar err error\n\tenv.timeout, env.hasTimeout, err = parseTimeoutEnv()\n\tif err != nil {\n\t\treturn envOverrides{}, err\n\t}\n\tif raw := os.Getenv(envOCRLLMExtraHeaders); raw != \"\" {\n\t\tenv.headers, err = ParseExtraHeaders(raw)\n\t\tif err != nil {\n\t\t\treturn envOverrides{}, fmt.Errorf(\"%s: %w\", envOCRLLMExtraHeaders, err)\n\t\t}\n\t}\n\treturn env, nil\n}\n\n// finalizeResolvedEndpoint stamps the source label, strips the model suffix and\n// applies the global env overrides, which win over config-file values.\nfunc finalizeResolvedEndpoint(source string, ep ResolvedEndpoint, env envOverrides) ResolvedEndpoint {\n\tif ep.Source == \"\" {\n\t\tep.Source = source\n\t}\n\tep.Model = stripModelSuffix(ep.Model)\n\tif env.hasTimeout {\n\t\tep.Timeout = env.timeout\n\t}\n\tif env.headers != nil {\n\t\tif ep.ExtraHeaders == nil {\n\t\t\tep.ExtraHeaders = env.headers","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/llm/resolver.go#L153-L189","documentation":"parseEnvOverrides runs before any resolution strategy and parses OCR_LLM_EXTRA_HEADERS with ParseExtraHeaders. If the raw string is not a valid header list, the error is wrapped as \"OCR_LLM_EXTRA_HEADERS: %w\" and aborts resolution up front — deliberately before any credential prompt (api_key_cmd) can run, so a typo cannot waste an interactive authentication.","triggerScenarios":"OCR_LLM_EXTRA_HEADERS is set to a string ParseExtraHeaders cannot parse — e.g. wrong separator, missing key or value, or unescaped characters (\"X-A=1;\" or \"X-A\" instead of \"X-A: 1,X-B: 2\").","commonSituations":"Copying a header string from curl -H syntax without adapting the separator format; trailing separators; setting the variable in shell rc with quoting that strips or mangles the delimiters.","solutions":["Fix the OCR_LLM_EXTRA_HEADERS format to match what ParseExtraHeaders expects (key/value pairs with the supported delimiters)","Check shell quoting: quote the whole value (OCR_LLM_EXTRA_HEADERS='X-A: 1, X-B: 2')","Unset the variable if extra headers are not needed"],"exampleFix":"// before\nexport OCR_LLM_EXTRA_HEADERS=\"X-Org\"\n// after\nexport OCR_LLM_EXTRA_HEADERS=\"X-Org: team1,X-Trace: on\"","handlingStrategy":"validation","validationCode":"raw := os.Getenv(\"OCR_LLM_EXTRA_HEADERS\")\nif raw != \"\" {\n    if _, err := llm.ParseExtraHeaders(raw); err != nil {\n        return fmt.Errorf(\"OCR_LLM_EXTRA_HEADERS invalid: %w\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"OCR_LLM_EXTRA_HEADERS\") {\n    return fmt.Errorf(\"check OCR_LLM_EXTRA_HEADERS format (key: value pairs): %w\", err)\n}","preventionTips":["Quote the variable value in shell: OCR_LLM_EXTRA_HEADERS='X-A: 1, X-B: 2'","Test-parse the string with ParseExtraHeaders before exporting it permanently","Avoid trailing separators and missing values"],"tags":["env-vars","parsing","http-headers"],"backgroundTag":"invalid-env-var-format","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}