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

  1. Inspect each configured header name/value and remove illegal characters or embedded newlines/CR-LF
  2. Remove duplicate headers that differ only in case (e.g. Authorization vs authorization)
  3. Ensure secrets go in the value field only, as a single-line token (e.g. 'Bearer sk-...')
  4. 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

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


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)