{"record":{"id":"f64348692fdecf3d","repo":"wavetermdev/waveterm","slug":"ai-model-is-required","errorCode":null,"errorMessage":"ai:model is required","messagePattern":"ai:model is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/anthropic/anthropic-convertmessage.go","lineNumber":34,"sourceCode":"\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/wavetermdev/waveterm/pkg/aiusechat/chatstore\"\n\t\"github.com/wavetermdev/waveterm/pkg/aiusechat/uctypes\"\n\t\"github.com/wavetermdev/waveterm/pkg/util/logutil\"\n\t\"github.com/wavetermdev/waveterm/pkg/util/utilfn\"\n\t\"github.com/wavetermdev/waveterm/pkg/wavebase\"\n)\n\n// these conversions are based off the anthropic spec\n// and the aiprompts/aisdk-uimessage-type.md doc (v5)\n\n// buildAnthropicHTTPRequest creates a complete HTTP request for the Anthropic API\nfunc buildAnthropicHTTPRequest(ctx context.Context, msgs []anthropicInputMessage, chatOpts uctypes.WaveChatOpts) (*http.Request, error) {\n\topts := chatOpts.Config\n\tif opts.Model == \"\" {\n\t\treturn nil, errors.New(\"ai:model is required\")\n\t}\n\tif chatOpts.ClientId == \"\" {\n\t\treturn nil, errors.New(\"chatOpts.ClientId is required\")\n\t}\n\n\t// Set defaults\n\tendpoint := opts.Endpoint\n\tif endpoint == \"\" {\n\t\treturn nil, errors.New(\"ai:endpoint is required\")\n\t}\n\n\tmaxTokens := opts.MaxTokens\n\tif maxTokens <= 0 {\n\t\tmaxTokens = AnthropicDefaultMaxTokens\n\t}\n\n\t// Convert messages to clear FileName fields from Source blocks\n\tconvertedMsgs := make([]anthropicInputMessage, len(msgs))","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/anthropic/anthropic-convertmessage.go#L16-L52","documentation":"buildAnthropicHTTPRequest requires opts.Model (from chatOpts.Config) because every Anthropic Messages API request must name a model. When Config.Model is empty the request cannot be built, so it fails fast with 'ai:model is required' before any network traffic.","triggerScenarios":"Calling RunAnthropicChatStep with chatOpts.Config.Model unset (empty string) — e.g. a WaveChatOpts built from partial config or a preset that never sets Model.","commonSituations":"Missing 'ai:model' key in user/term config; config loaded from an env or profile that predates the model field; constructing Config{} programmatically in tests; typo'd config key so the model value lands elsewhere.","solutions":["Set chatOpts.Config.Model to a valid Anthropic model id (e.g. \"claude-sonnet-4-20250514\") before calling.","If model comes from config, add/fix the 'ai:model' setting in the Wave term/block config and reload.","Add pre-call validation that surfaces the empty Model at configuration time."],"exampleFix":"// before\nchatOpts := uctypes.WaveChatOpts{Config: uctypes.AIConfig{}}\n\n// after\nchatOpts := uctypes.WaveChatOpts{Config: uctypes.AIConfig{Model: \"claude-sonnet-4-20250514\"}}","handlingStrategy":"validation","validationCode":"if chatOpts.Config.Model == \"\" {\n    return fmt.Errorf(\"config error: ai:model must be set before calling anthropic chat\")\n}","typeGuard":"func hasModel(cfg uctypes.AIConfig) bool { return cfg.Model != \"\" }","tryCatchPattern":"_, _, _, err := RunAnthropicChatStep(ctx, sse, chatOpts, cont)\nif err != nil && strings.Contains(err.Error(), \"ai:model is required\") {\n    return fmt.Errorf(\"set 'ai:model' in your term/block config: %w\", err)\n}","preventionTips":["Set ai:model in your Wave AI config and verify with a config dump at startup.","Centralize WaveChatOpts construction in one helper that asserts required fields.","Keep a list of valid Anthropic model ids in your code/constants."],"tags":["configuration","validation","anthropic"],"backgroundTag":"missing-required-config","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}