alibaba/open-code-review · error

invalid extra header %q: expected key=value

Error message

invalid extra header %q: expected key=value

What it means

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.

Source

Thrown at internal/llm/resolver.go:869

func ParseExtraHeaders(raw string) (map[string]string, error) {
	if raw == "" {
		return nil, nil
	}

	pairs, err := splitHeaderPairs(raw)
	if err != nil {
		return nil, err
	}

	result := make(map[string]string)
	for _, pair := range pairs {
		pair = strings.TrimSpace(pair)
		if pair == "" {
			continue
		}
		parts := strings.SplitN(pair, "=", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("invalid extra header %q: expected key=value", pair)
		}
		key := strings.TrimSpace(parts[0])
		value := strings.TrimSpace(parts[1])
		if key == "" {
			return nil, fmt.Errorf("invalid extra header %q: empty header name", pair)
		}
		if reservedHeaders[strings.ToLower(key)] {
			return nil, fmt.Errorf("extra header %q conflicts with a reserved header; use the dedicated config field instead", key)
		}
		if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' {
			value = value[1 : len(value)-1]
		}
		result[key] = value
	}
	return result, nil
}

// splitHeaderPairs splits a comma-separated string while respecting double-quoted segments.

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Rewrite the entry as key=value, e.g. extra_headers = ["X-Custom-Trace=abc123"]
  2. If the header needs no value, use an empty value explicitly: "X-Custom-Trace="
  3. Check the separator: HTTP colon syntax ("Key: value") is not accepted here — only '='
  4. Trim stray whitespace/quotes so the entry isn't split unexpectedly

Example fix

// before (config file)
extra_headers = ["X-Custom-Trace"]
// after
extra_headers = ["X-Custom-Trace=abc123"]
Defensive patterns

Strategy: validation

Validate before calling

// validate extra_headers entries before running ocr
for _, h := range cfg.ExtraHeaders {
    if !strings.Contains(h, "=") {
        return fmt.Errorf("extra header %q must be key=value", h)
    }
    if strings.TrimSpace(strings.SplitN(h, "=", 2)[0]) == "" {
        return fmt.Errorf("extra header %q has empty key", h)
    }
}

Try / catch

if _, _, err := llm.ResolveEndpoint(cfg, ""); err != nil {
    var ehErr = "invalid extra header"
    if strings.Contains(err.Error(), ehErr) {
        // print the offending entry quoted in the error and fix the config
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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 '='.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/5c5b52d5fc27a76f. Report an issue: GitHub.