{"record":{"id":"5daa9e482577d5c8","repo":"JuliusBrussee/caveman","slug":"unsupported-corpus-provider-q","errorCode":null,"errorMessage":"unsupported corpus provider %q","messagePattern":"unsupported corpus provider %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/corpus.go","lineNumber":722,"sourceCode":"\t\tif err := encoder.Encode(message); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn hex.EncodeToString(digest.Sum(nil)), nil\n}\n\nfunc corpusProviderBody(provider ProviderConfig, messages []CorpusMessage) ([]byte, error) {\n\tswitch provider.Provider {\n\tcase \"openai\":\n\t\treturn json.Marshal(map[string]any{\"model\": provider.Model, \"max_completion_tokens\": 256, \"messages\": messages})\n\tcase \"anthropic\":\n\t\treturn anthropicCorpusBody(provider.Model, messages)\n\tcase \"bedrock\":\n\t\treturn bedrockCorpusBody(messages)\n\tcase \"gemini\":\n\t\treturn geminiCorpusBody(messages)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported corpus provider %q\", provider.Provider)\n\t}\n}\n\nfunc contentText(content json.RawMessage) (string, error) {\n\tif len(content) == 0 || bytes.Equal(bytes.TrimSpace(content), []byte(\"null\")) {\n\t\treturn \"\", nil\n\t}\n\tvar text string\n\tif err := json.Unmarshal(content, &text); err == nil {\n\t\treturn text, nil\n\t}\n\treturn \"\", errors.New(\"corpus provider conversion supports string or null content\")\n}\n\nfunc anthropicCorpusBody(model string, messages []CorpusMessage) ([]byte, error) {\n\tvar system []string\n\tconverted := make([]any, 0, len(messages))\n\tfor _, message := range messages {","sourceCodeStart":704,"sourceCodeEnd":740,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/corpus.go#L704-L740","documentation":"corpusProviderBody switches on provider.Provider and only knows 'openai', 'anthropic', 'bedrock', and 'gemini'. Any other value — including case variants and typos — hits the default branch and is rejected with the offending value quoted.","triggerScenarios":"Building a trace or provider body with ProviderConfig.Provider set to anything except the four exact lowercase strings, e.g. 'OpenAI', 'azure', or an empty string.","commonSituations":"Config file copied from another tool using different provider names ('azure-openai', 'vertex'); trailing whitespace or capitalization in YAML/JSON config; defaulting logic that leaves Provider empty when unset.","solutions":["Set Provider to exactly one of: openai, anthropic, bedrock, gemini.","Trim and lowercase the provider value when loading config.","Fail fast at config-load time with an allow-list check so the typo surfaces before a long replay starts."],"exampleFix":"// before\nprovider := ProviderConfig{Provider: \"OpenAI\", Model: \"gpt-4o\"}\n// after\nprovider := ProviderConfig{Provider: \"openai\", Model: \"gpt-4o\"}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func supportedProvider(p string) bool {\n\tswitch p {\n\tcase \"openai\", \"anthropic\", \"bedrock\", \"gemini\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"body, err := corpusProviderBody(provider, msgs)\nif err != nil && strings.Contains(err.Error(), \"unsupported corpus provider\") {\n\tlog.Fatalf(\"fix provider config: %v\", err)\n}","preventionTips":["Centralize provider names as constants and reuse them in config.","Lowercase/trim provider strings when parsing config files.","Fail config validation at startup, not mid-replay."],"tags":["go","configuration","provider","cachebench"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}