wavetermdev/waveterm · error

message %d: expected *anthropicChatMessage, got %T

Error message

message %d: expected *anthropicChatMessage, got %T

What it means

ConvertAIChatToUIChat type-asserts each entry of aiChat.NativeMessages to *anthropicChatMessage. A native message of any other concrete type cannot be converted to the Anthropic UI representation, so the loop fails fast with the index and dynamic type of the offending message.

Source

Thrown at pkg/aiusechat/anthropic/anthropic-convertmessage.go:825

	return &anthropicChatMessage{
		MessageId: uuid.New().String(),
		Role:      "user",
		Content:   contentBlocks,
	}, nil
}

// ConvertAIChatToUIChat converts an AIChat to a UIChat for Anthropic
func ConvertAIChatToUIChat(aiChat uctypes.AIChat) (*uctypes.UIChat, error) {
	if aiChat.APIType != uctypes.APIType_AnthropicMessages {
		return nil, fmt.Errorf("APIType must be '%s', got '%s'", uctypes.APIType_AnthropicMessages, aiChat.APIType)
	}

	uiMessages := make([]uctypes.UIMessage, 0, len(aiChat.NativeMessages))

	for i, nativeMsg := range aiChat.NativeMessages {
		anthropicMsg, ok := nativeMsg.(*anthropicChatMessage)
		if !ok {
			return nil, fmt.Errorf("message %d: expected *anthropicChatMessage, got %T", i, nativeMsg)
		}

		uiMsg := anthropicMsg.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
}

func GetFunctionCallInputByToolCallId(aiChat uctypes.AIChat, toolCallId string) *uctypes.AIFunctionCallInput {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Only include *anthropicChatMessage values in NativeMessages for Anthropic chats
  2. Rebuild the chat messages via the Anthropic converter so they get the right concrete type
  3. Filter or convert foreign messages before calling ConvertAIChatToUIChat

Example fix

// before
aiChat.NativeMessages = append(aiChat.NativeMessages, someOpenAIMessage)
// after
anthMsg, err := anthropic.ConvertAIMessageToAnthropicChatMessage(aiMsg)
aiChat.NativeMessages = append(aiChat.NativeMessages, anthMsg)
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

func asAnthropicMessage(m uctypes.NativeMessage) (*anthropicChatMessage, bool) {
    msg, ok := m.(*anthropicChatMessage)
    return msg, ok
}

Try / catch

uiChat, err := anthropic.ConvertAIChatToUIChat(aiChat)
if err != nil {
    var idx int; var typ string
    if _, scan := fmt.Sscanf(err.Error(), "message %d: expected *anthropicChatMessage, got %s", &idx, &typ); scan == nil {
        return fmt.Errorf("chat contains foreign message at index %d (%s)", idx, typ)
    }
    return err
}

Prevention

When it happens

Trigger: ConvertAIChatToUIChat called on an AIChat whose NativeMessages slice contains a message not created by this package (e.g. *openaiChatMessage, a generic struct, or nil of a different type).

Common situations: Appending messages from another provider into a shared NativeMessages slice; custom message wrappers stored in the chat; cache deserialization yielding the wrong concrete type.

Related errors


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