wavetermdev/waveterm · error

message %d: expected *OpenAIChatMessage, got %T

Error message

message %d: expected *OpenAIChatMessage, got %T

What it means

The chat's NativeMessages must all be *OpenAIChatMessage for the OpenAI UI converter; if any element is a different native message type the type assertion fails and this error reports the index and concrete Go type found.

Source

Thrown at pkg/aiusechat/openai/openai-convertmessage.go:559

		return nil
	}
	return &uctypes.UIMessage{
		ID:    m.MessageId,
		Role:  role,
		Parts: parts,
	}
}

// ConvertAIChatToUIChat converts an AIChat to a UIChat for OpenAI
func ConvertAIChatToUIChat(aiChat uctypes.AIChat) (*uctypes.UIChat, error) {
	if aiChat.APIType != uctypes.APIType_OpenAIResponses {
		return nil, fmt.Errorf("APIType must be '%s', got '%s'", uctypes.APIType_OpenAIResponses, aiChat.APIType)
	}
	uiMessages := make([]uctypes.UIMessage, 0, len(aiChat.NativeMessages))
	for i, nativeMsg := range aiChat.NativeMessages {
		openaiMsg, ok := nativeMsg.(*OpenAIChatMessage)
		if !ok {
			return nil, fmt.Errorf("message %d: expected *OpenAIChatMessage, got %T", i, nativeMsg)
		}
		uiMsg := openaiMsg.convertToUIMessage()
		if uiMsg != nil {
			uiMessages = append(uiMessages, *uiMsg)
		}
	}
	return &uctypes.UIChat{
		ChatId:     aiChat.ChatId,
		APIType:    aiChat.APIType,
		Model:      aiChat.Model,
		APIVersion: aiChat.APIVersion,
		Messages:   uiMessages,
	}, nil
}

// GetFunctionCallInputByToolCallId returns the OpenAIFunctionCallInput associated with the given ToolCallId,
// or nil if not found in the AIChat
func GetFunctionCallInputByToolCallId(aiChat uctypes.AIChat, toolCallId string) *OpenAIFunctionCallInput {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure all native messages in the chat were produced by the OpenAI Responses backend
  2. Filter or split messages by type before conversion
  3. Use the converter matching the actual message type stored in the chat

Example fix

// before
openaiMsg, ok := nativeMsg.(*OpenAIChatMessage) // fails for other types
// after
switch m := nativeMsg.(type) {
case *OpenAIChatMessage:
    // handle
default:
    return fmt.Errorf("unsupported native message %T", nativeMsg)
}
Defensive patterns

Strategy: type-guard

Validate before calling

for i, m := range aiChat.NativeMessages {
    if _, ok := m.(*OpenAIChatMessage); !ok {
        return fmt.Errorf("message %d not an OpenAIChatMessage", i)
    }
}

Type guard

func isOpenAIChatMessage(m any) bool { _, ok := m.(*OpenAIChatMessage); return ok }

Prevention

When it happens

Trigger: aiChat.NativeMessages contains messages produced by another backend (e.g. *anthropic.ChatMessage or *StoredChatMessage) while ConvertAIChatToUIChat expects *OpenAIChatMessage for every entry.

Common situations: Appending native messages from different backends to the same chat; loading mixed persisted history; calling the OpenAI converter on a chat handled by a different backend's RunChatStep (e.g. StoredChatMessage in openaichat).

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/03c7d5d26c640c51. Report an issue: GitHub.