{"record":{"id":"2c5599e4ea1c4f4b","repo":"alibaba/open-code-review","slug":"custom-provider-q-w","errorCode":null,"errorMessage":"custom provider %q: %w","messagePattern":"custom provider %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/llm/resolver.go","lineNumber":457,"sourceCode":"\t\tif entry.Protocol != \"\" {\n\t\t\tnormalized := NormalizeProtocol(entry.Protocol)\n\t\t\tif err := ValidateProtocol(normalized); err != nil {\n\t\t\t\treturn ResolvedEndpoint{}, false, fmt.Errorf(\"provider %q: %w\", cfg.Provider, err)\n\t\t\t}\n\t\t\tprotocol = normalized\n\t\t}\n\t} else {\n\t\t// Custom provider: protocol is always required; model can come from\n\t\t// cfg.Model. url is required for every protocol that names an HTTP\n\t\t// endpoint, which is all of them except bedrock — there the region\n\t\t// decides the host, so demanding a url would mean storing a value the\n\t\t// client never reads.\n\t\tif entry.Protocol == \"\" {\n\t\t\treturn ResolvedEndpoint{}, false, fmt.Errorf(\"custom provider %q requires a protocol field\", cfg.Provider)\n\t\t}\n\t\tnormalized := NormalizeProtocol(entry.Protocol)\n\t\tif err := ValidateProtocol(normalized); err != nil {\n\t\t\treturn ResolvedEndpoint{}, false, fmt.Errorf(\"custom provider %q: %w\", cfg.Provider, err)\n\t\t}\n\t\tif normalized != ProtocolAnthropicBedrock && entry.URL == \"\" {\n\t\t\treturn ResolvedEndpoint{}, false, fmt.Errorf(\"custom provider %q requires a url field for protocol %q\", cfg.Provider, normalized)\n\t\t}\n\t\turl = entry.URL\n\t\tprotocol = normalized\n\t}\n\n\t// Ambient auth follows the protocol actually in force, which is why this is\n\t// resolved after the override above rather than read off the preset. A preset\n\t// declares ambient auth (AmbientAuth), but an entry may override the preset's\n\t// protocol: a bedrock preset switched to \"openai\" speaks a protocol with no\n\t// SigV4 signing and needs a token like anything else. Conversely an entry\n\t// that selects the bedrock protocol explicitly signs its requests whatever\n\t// the preset says.\n\tambientAuth := protocol == ProtocolAnthropicBedrock ||\n\t\t(isPreset && preset.AmbientAuth && entry.Protocol == \"\")\n","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/llm/resolver.go#L439-L475","documentation":"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.","triggerScenarios":"A custom_providers entry sets \"protocol\" to a value that normalizes to nothing valid, e.g. \"openai chat\", \"OpenAI-Compat\", \"gpt-4o\".","commonSituations":"Describing the model or compatibility layer instead of the wire protocol; copying a vendor marketing name; trailing punctuation picked up from docs.","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"],"exampleFix":"// before\n\"custom_providers\": { \"my-gateway\": { \"protocol\": \"OpenAI-Compatible\", \"url\": \"https://gw.internal/v1\" } }\n\n// after\n\"custom_providers\": { \"my-gateway\": { \"protocol\": \"openai\", \"url\": \"https://gw.internal/v1\" } }","handlingStrategy":"validation","validationCode":"var valid = map[string]bool{\"anthropic\": true, \"openai\": true, \"openai-responses\": true, \"anthropic-bedrock\": true}\nif p := entry.Protocol; p != \"\" && !valid[p] {\n    return fmt.Errorf(\"custom provider protocol %q is not one of anthropic|openai|openai-responses|anthropic-bedrock\", p)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["configuration","custom-provider","protocol"],"backgroundTag":"invalid-protocol-value","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}