{"record":{"id":"d8ad84bd2143ea1d","repo":"siyuan-note/siyuan","slug":"invalid-ai-provider-http-headers","errorCode":null,"errorMessage":"invalid AI provider HTTP headers","messagePattern":"invalid AI provider HTTP headers","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/openai_headers.go","lineNumber":26,"sourceCode":"\n\t\"github.com/siyuan-note/httpclient\"\n\t\"golang.org/x/net/http/httpguts\"\n)\n\ntype aiProviderHeaderTransport struct {\n\tbase    http.RoundTripper\n\torigin  *url.URL\n\theaders http.Header\n\terr     error\n}\n\n// ValidateAIProviderHeaders 在保存配置和发送请求前校验请求头，不在错误中包含凭据。\nfunc ValidateAIProviderHeaders(headers map[string]string) error {\n\tnames := map[string]bool{}\n\tfor name, value := range headers {\n\t\tkey := http.CanonicalHeaderKey(name)\n\t\tif !httpguts.ValidHeaderFieldName(name) || !httpguts.ValidHeaderFieldValue(value) || names[key] {\n\t\t\treturn errors.New(\"invalid AI provider HTTP headers\")\n\t\t}\n\t\tnames[key] = true\n\t}\n\treturn nil\n}\n\n// newAIProviderHTTPClient 为供应商请求注入自定义头，覆盖同名默认头，并限制在配置的源站内。\nfunc newAIProviderHTTPClient(baseURL string, headers ...map[string]string) *http.Client {\n\tif len(headers) == 0 || len(headers[0]) == 0 {\n\t\treturn httpclient.NewUserAgentClient(nil)\n\t}\n\torigin, err := url.Parse(baseURL)\n\ttransport := &aiProviderHeaderTransport{\n\t\tbase: httpclient.NewTransport(false), origin: origin, headers: http.Header{}, err: err,\n\t}\n\tif err := ValidateAIProviderHeaders(headers[0]); err != nil {\n\t\ttransport.err = err\n\t}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/openai_headers.go#L8-L44","documentation":"ValidateAIProviderHeaders checks user-configured AI provider HTTP headers before saving config or sending requests. It rejects names that are not valid header field names, values that are not valid header field values, or duplicate names after canonicalization. The error deliberately omits header values so credentials are never leaked in error messages.","triggerScenarios":"Saving AI provider configuration with a malformed header name (illegal characters, whitespace), a value containing invalid bytes or line breaks, or two header keys that canonicalize to the same name (e.g. 'x-key' and 'X-Key'); also raised by resolveAIProvider and newAIProviderHTTPClient at request time.","commonSituations":"Pasting a header line including a colon or 'Bearer ' prefix into the name field; multiline values from copying config; accidental duplicate Authorization headers defined twice with different casing.","solutions":["Inspect each configured header name/value and remove illegal characters or embedded newlines/CR-LF","Remove duplicate headers that differ only in case (e.g. Authorization vs authorization)","Ensure secrets go in the value field only, as a single-line token (e.g. 'Bearer sk-...')","Re-save the AI provider config so validation passes before the next request"],"exampleFix":"// before\nheaders := map[string]string{\"Authorization\": \"Bearer sk-abc\\nX-Extra: 1\", \"x-key\": \"v\", \"X-Key\": \"v\"}\nutil.ValidateAIProviderHeaders(headers) // error\n// after\nheaders := map[string]string{\"Authorization\": \"Bearer sk-abc\", \"X-Extra\": \"1\", \"X-Key\": \"v\"}\nif err := util.ValidateAIProviderHeaders(headers); err != nil { /* fix config */ }","handlingStrategy":"validation","validationCode":"for name, value := range headers {\n  if !httpguts.ValidHeaderFieldName(name) || !httpguts.ValidHeaderFieldValue(value) {\n    return fmt.Errorf(\"header %q has invalid name or value\", name)\n  }\n}\nseen := map[string]bool{}\nfor name := range headers {\n  k := http.CanonicalHeaderKey(name)\n  if seen[k] { return fmt.Errorf(\"duplicate header %q\", k) }\n  seen[k] = true\n}","typeGuard":null,"tryCatchPattern":"if err := util.ValidateAIProviderHeaders(headers); err != nil {\n  return fmt.Errorf(\"rejecting provider config: %w\", err)\n}","preventionTips":["Keep header names token-only (letters, digits, hyphens) without colons or spaces","Put full credentials including 'Bearer ' prefix in the value, never the name","Never embed newlines or CR/LF in header values (request-smuggling protection)","Avoid defining the same header twice with different casing"],"tags":["http","headers","validation","ai-provider"],"backgroundTag":"invalid-argument-format","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}