alibaba/open-code-review · error
custom provider %q: %w
Error message
custom provider %q: %w
What it means
Wrapper error ("custom provider %q: %w") from tryProviderConfig when a custom provider's declared protocol fails ValidateProtocol after normalization. Custom providers have no preset defaults, so their protocol string must exactly name a supported protocol or resolution aborts.
Source
Thrown at internal/llm/resolver.go:457
if entry.Protocol != "" {
normalized := NormalizeProtocol(entry.Protocol)
if err := ValidateProtocol(normalized); err != nil {
return ResolvedEndpoint{}, false, fmt.Errorf("provider %q: %w", cfg.Provider, err)
}
protocol = normalized
}
} else {
// Custom provider: protocol is always required; model can come from
// cfg.Model. url is required for every protocol that names an HTTP
// endpoint, which is all of them except bedrock — there the region
// decides the host, so demanding a url would mean storing a value the
// client never reads.
if entry.Protocol == "" {
return ResolvedEndpoint{}, false, fmt.Errorf("custom provider %q requires a protocol field", cfg.Provider)
}
normalized := NormalizeProtocol(entry.Protocol)
if err := ValidateProtocol(normalized); err != nil {
return ResolvedEndpoint{}, false, fmt.Errorf("custom provider %q: %w", cfg.Provider, err)
}
if normalized != ProtocolAnthropicBedrock && entry.URL == "" {
return ResolvedEndpoint{}, false, fmt.Errorf("custom provider %q requires a url field for protocol %q", cfg.Provider, normalized)
}
url = entry.URL
protocol = normalized
}
// Ambient auth follows the protocol actually in force, which is why this is
// resolved after the override above rather than read off the preset. A preset
// declares ambient auth (AmbientAuth), but an entry may override the preset's
// protocol: a bedrock preset switched to "openai" speaks a protocol with no
// SigV4 signing and needs a token like anything else. Conversely an entry
// that selects the bedrock protocol explicitly signs its requests whatever
// the preset says.
ambientAuth := protocol == ProtocolAnthropicBedrock ||
(isPreset && preset.AmbientAuth && entry.Protocol == "")
View on GitHub (pinned to 5cf97d0d15)
Solutions
- Use exactly one of: "anthropic", "openai", "openai-responses", "anthropic-bedrock"
- For OpenAI-compatible gateways use "openai" regardless of vendor branding
- Verify with a clean quote-free plain string in config.json
Example fix
// before
"custom_providers": { "my-gateway": { "protocol": "OpenAI-Compatible", "url": "https://gw.internal/v1" } }
// after
"custom_providers": { "my-gateway": { "protocol": "openai", "url": "https://gw.internal/v1" } } Defensive patterns
Strategy: validation
Validate before calling
var valid = map[string]bool{"anthropic": true, "openai": true, "openai-responses": true, "anthropic-bedrock": true}
if p := entry.Protocol; p != "" && !valid[p] {
return fmt.Errorf("custom provider protocol %q is not one of anthropic|openai|openai-responses|anthropic-bedrock", p)
} Prevention
- Copy protocol names verbatim from the documentation, lowercase
- Do not put vendor/model names in the protocol field
- Add a config lint step that checks protocol values against the allowed set
When it happens
Trigger: A custom_providers entry sets "protocol" to a value that normalizes to nothing valid, e.g. "openai chat", "OpenAI-Compat", "gpt-4o".
Common situations: Describing the model or compatibility layer instead of the wire protocol; copying a vendor marketing name; trailing punctuation picked up from docs.
Related errors
- custom provider %q requires a protocol field
- OCR environment: %w
- provider %q: %w
- load rules: %w
- resolve LLM endpoint: %w
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/2c5599e4ea1c4f4b.
Report an issue: GitHub.