{"record":{"id":"f8c8b429f49c8a3a","repo":"wavetermdev/waveterm","slug":"expected-openaichatmessage-got-t","errorCode":null,"errorMessage":"expected OpenAIChatMessage, got %T","messagePattern":"expected OpenAIChatMessage, got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/openai/openai-backend.go","lineNumber":510,"sourceCode":"\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, time.Duration(chatOpts.Config.TimeoutMs)*time.Millisecond)\n\t\tdefer cancel()\n\t}\n\n\t// Validate continuation if provided\n\tif cont != nil {\n\t\tif !uctypes.AreModelsCompatible(chat.APIType, chatOpts.Config.Model, cont.Model) {\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"cannot continue with a different model, model:%q, cont-model:%q\", chatOpts.Config.Model, cont.Model)\n\t\t}\n\t}\n\n\t// Convert GenAIMessages to input objects (OpenAIMessage or OpenAIFunctionCallInput)\n\tvar inputs []any\n\tfor _, genMsg := range chat.NativeMessages {\n\t\t// Cast to OpenAIChatMessage\n\t\tchatMsg, ok := genMsg.(*OpenAIChatMessage)\n\t\tif !ok {\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"expected OpenAIChatMessage, got %T\", genMsg)\n\t\t}\n\n\t\t// Convert to appropriate input type based on what's populated\n\t\tif chatMsg.Message != nil {\n\t\t\t// Clean message to remove preview URLs\n\t\t\tcleanedMsg := chatMsg.Message.cleanAndCopy()\n\t\t\tinputs = append(inputs, *cleanedMsg)\n\t\t} else if chatMsg.FunctionCall != nil {\n\t\t\tcleanedFunctionCall := chatMsg.FunctionCall.clean()\n\t\t\tinputs = append(inputs, *cleanedFunctionCall)\n\t\t} else if chatMsg.FunctionCallOutput != nil {\n\t\t\tinputs = append(inputs, *chatMsg.FunctionCallOutput)\n\t\t}\n\t}\n\n\treq, err := buildOpenAIHTTPRequest(ctx, inputs, chatOpts, cont)\n\tif err != nil {\n\t\treturn nil, nil, nil, err","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/openai/openai-backend.go#L492-L528","documentation":"While converting chat.NativeMessages into OpenAI inputs, RunOpenAIChatStep type-asserts every native message to *OpenAIChatMessage and returns \"expected OpenAIChatMessage, got %T\" at openai-backend.go:510 if any message has a different concrete type. The OpenAI backend assumes its chats contain only OpenAI-native messages; a foreign message type indicates the chat history was polluted by another backend or a bug.","triggerScenarios":"A chat whose NativeMessages slice contains a non-*OpenAIChatMessage entry — e.g. messages inserted by a different backend (azure/other provider) under the same chat id, or test/mock message types appended to the store.","commonSituations":"Switching provider backends while keeping the same ChatId so another backend wrote its message type into the same chat; custom code posting messages directly to chatstore with the wrong native type; mixed-provider chat reuse after a config change.","solutions":["Start a new chat for the new backend instead of reusing a chat id across API types","Inspect chat.NativeMessages (%T of each) to find the offending message type and remove it from the store","Ensure all PostMessage calls for this chat use *OpenAIChatMessage native payloads","If you control the loop, skip (continue) non-matching messages instead of failing the whole step — as UpdateToolUseData does"],"exampleFix":"// before\nchatMsg, ok := genMsg.(*OpenAIChatMessage)\nif !ok {\n    return nil, nil, nil, fmt.Errorf(\"expected OpenAIChatMessage, got %T\", genMsg)\n}\n// after\nchatMsg, ok := genMsg.(*OpenAIChatMessage)\nif !ok {\n    log.Printf(\"skipping non-openai message %T in chat %s\", genMsg, chatOpts.ChatId)\n    continue\n}","handlingStrategy":"type-guard","validationCode":"chat := chatstore.DefaultChatStore.Get(chatOpts.ChatId)\nif chat != nil {\n    for _, m := range chat.NativeMessages {\n        if _, ok := m.(*OpenAIChatMessage); !ok {\n            return fmt.Errorf(\"chat %s contains foreign message type %T\", chatOpts.ChatId, m)\n        }\n    }\n}","typeGuard":"func allMessagesAreOpenAI(chatId string) bool {\n    chat := chatstore.DefaultChatStore.Get(chatId)\n    if chat == nil {\n        return false\n    }\n    for _, m := range chat.NativeMessages {\n        if _, ok := m.(*OpenAIChatMessage); !ok {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"if _, _, _, err := RunOpenAIChatStep(ctx, sse, chatOpts, nil); err != nil {\n    if strings.Contains(err.Error(), \"expected OpenAIChatMessage\") {\n        // chat history polluted by another backend; start a new chat\n        return nil\n    }\n    return err\n}","preventionTips":["Never reuse a ChatId across different provider backends","Only post *OpenAIChatMessage natives to OpenAI-managed chats","Audit chatstore writes in custom code for native type correctness","Sanitize or skip foreign messages before stepping instead of failing"],"tags":["go","type-assertion","chatstore","openai"],"backgroundTag":"unexpected-message-type","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}