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

  1. Use exactly one of: "anthropic", "openai", "openai-responses", "anthropic-bedrock"
  2. For OpenAI-compatible gateways use "openai" regardless of vendor branding
  3. 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

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


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