{"record":{"id":"cd69736903d513a9","repo":"wavetermdev/waveterm","slug":"chatopts-clientid-is-required-cd6973","errorCode":null,"errorMessage":"chatOpts.ClientId is required","messagePattern":"chatOpts\\.ClientId is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/openai/openai-convertmessage.go","lineNumber":206,"sourceCode":"\t}\n}\n\n// buildOpenAIHTTPRequest creates a complete HTTP request for the OpenAI API\nfunc buildOpenAIHTTPRequest(ctx context.Context, inputs []any, chatOpts uctypes.WaveChatOpts, cont *uctypes.WaveContinueResponse) (*http.Request, error) {\n\topts := chatOpts.Config\n\n\t// If continuing from premium rate limit, downgrade to default model and medium thinking\n\t// (medium is more widely supported than low across different models)\n\tif cont != nil && cont.ContinueFromKind == uctypes.StopKindPremiumRateLimit {\n\t\topts.Model = uctypes.DefaultOpenAIModel\n\t\topts.ThinkingLevel = uctypes.ThinkingLevelMedium\n\t}\n\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 = OpenAIDefaultMaxTokens\n\t}\n\n\t// injected data\n\tif chatOpts.TabState != \"\" {\n\t\tappendToLastUserMessage(inputs, chatOpts.TabState)\n\t}\n\tif chatOpts.PlatformInfo != \"\" {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/openai/openai-convertmessage.go#L188-L224","documentation":"buildOpenAIHTTPRequest requires chatOpts.ClientId to be set because the client id ties the streaming response back to the requesting client/session (used for routing SSE output). 'chatOpts.ClientId is required' is thrown when it is empty — a caller-side wiring problem, not a user configuration issue.","triggerScenarios":"RunOpenAIChatStep is called with chatOpts.ClientId == \"\": the caller that constructs WaveChatOpts never populated ClientId, the block/controller id was not available at construction time, or opts were copied from a template struct with an empty ClientId.","commonSituations":"Programmatic/backend use of the AI layer outside the normal block UI flow; tests calling RunOpenAIChatStep with a minimal WaveChatOpts; refactors that changed how client/block ids are generated leaving the field unset.","solutions":["Set chatOpts.ClientId to the requesting block/controller id before calling RunOpenAIChatStep","Trace where WaveChatOpts is constructed and ensure ClientId is populated from the active client context","In tests or headless usage, supply a synthetic but non-empty ClientId","Add an early assertion/logging where WaveChatOpts is built to catch empty ClientId during development"],"exampleFix":"// before\nchatOpts := uctypes.WaveChatOpts{Config: cfg} // ClientId empty\n// after\nchatOpts := uctypes.WaveChatOpts{Config: cfg, ClientId: blockId}\nif chatOpts.ClientId == \"\" {\n    return fmt.Errorf(\"cannot start chat step without ClientId\")\n}","handlingStrategy":"validation","validationCode":"if chatOpts.ClientId == \"\" {\n    return fmt.Errorf(\"cannot run OpenAI chat step: ClientId missing\")\n}","typeGuard":null,"tryCatchPattern":"if err != nil && err.Error() == \"chatOpts.ClientId is required\" {\n    return fmt.Errorf(\"internal error: chat options built without a ClientId; check WaveChatOpts construction\")\n}","preventionTips":["Populate ClientId from the active block/controller context whenever WaveChatOpts is built","Never copy WaveChatOpts from a zero-value template without re-setting identity fields","Assert ClientId is non-empty where chat options are constructed in tests"],"tags":["openai","validation","client-id","programmer-error"],"backgroundTag":"missing-config-value","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}