siyuan-note/siyuan · error
invalid AI provider HTTP headers
Error message
invalid AI provider HTTP headers
What it means
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.
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
Example fix
// before
headers := map[string]string{"Authorization": "Bearer sk-abc\nX-Extra: 1", "x-key": "v", "X-Key": "v"}
util.ValidateAIProviderHeaders(headers) // error
// after
headers := map[string]string{"Authorization": "Bearer sk-abc", "X-Extra": "1", "X-Key": "v"}
if err := util.ValidateAIProviderHeaders(headers); err != nil { /* fix config */ } Defensive patterns
Strategy: validation
Validate before calling
for name, value := range headers {
if !httpguts.ValidHeaderFieldName(name) || !httpguts.ValidHeaderFieldValue(value) {
return fmt.Errorf("header %q has invalid name or value", name)
}
}
seen := map[string]bool{}
for name := range headers {
k := http.CanonicalHeaderKey(name)
if seen[k] { return fmt.Errorf("duplicate header %q", k) }
seen[k] = true
} Try / catch
if err := util.ValidateAIProviderHeaders(headers); err != nil {
return fmt.Errorf("rejecting provider config: %w", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Field [ ] is required
- Field [ ] must not be empty
- invalid plugin service HTTP status
- invalid request:
- list models HTTP
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/d8ad84bd2143ea1d.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/openai_headers.go:26
"github.com/siyuan-note/httpclient"
"golang.org/x/net/http/httpguts"
)
type aiProviderHeaderTransport struct {
base http.RoundTripper
origin *url.URL
headers http.Header
err error
}
// ValidateAIProviderHeaders 在保存配置和发送请求前校验请求头,不在错误中包含凭据。
func ValidateAIProviderHeaders(headers map[string]string) error {
names := map[string]bool{}
for name, value := range headers {
key := http.CanonicalHeaderKey(name)
if !httpguts.ValidHeaderFieldName(name) || !httpguts.ValidHeaderFieldValue(value) || names[key] {
return errors.New("invalid AI provider HTTP headers")
}
names[key] = true
}
return nil
}
// newAIProviderHTTPClient 为供应商请求注入自定义头,覆盖同名默认头,并限制在配置的源站内。
func newAIProviderHTTPClient(baseURL string, headers ...map[string]string) *http.Client {
if len(headers) == 0 || len(headers[0]) == 0 {
return httpclient.NewUserAgentClient(nil)
}
origin, err := url.Parse(baseURL)
transport := &aiProviderHeaderTransport{
base: httpclient.NewTransport(false), origin: origin, headers: http.Header{}, err: err,
}
if err := ValidateAIProviderHeaders(headers[0]); err != nil {
transport.err = err
}View on GitHub (pinned to 9f775e8a12)