Skip to content

createUserUIMessage doesn't spread partCommon on string content path #234

@Jake-qp

Description

@Jake-qp

Bug

In UIMessages.js, createUserUIMessage doesn't spread partCommon (which carries providerMetadata) onto parts when the message has simple string content.

Line 211 (string content path):

parts: [{ type: "text", text: content }]  // partCommon NOT spread

Line 216 (array content path):

parts: content.map((part) => ({ ...partCommon, ...part }))  // partCommon IS spread ✓

Compare with createSystemUIMessage and createAssistantUIMessage which both correctly spread partCommon on all paths.

Impact

When using saveMessage with prompt (string) + metadata.providerMetadata, the providerMetadata is stored in the DB but lost when constructing UIMessage parts. Consumers reading parts[].providerMetadata get undefined.

Reproduction

const { messageId } = await saveMessage(ctx, components.agent, {
  threadId,
  prompt: "Hello",  // string → hits line 211
  metadata: {
    providerMetadata: {
      myApp: { customField: "value" },
    },
  },
});

// Later, in useUIMessages:
// message.parts[0].providerMetadata → undefined (expected: { myApp: { customField: "value" } })

Workaround

Use message with array content instead of prompt:

const { messageId } = await saveMessage(ctx, components.agent, {
  threadId,
  message: {
    role: "user",
    content: [{ type: "text", text: "Hello" }],  // array → hits line 216
  },
  metadata: {
    providerMetadata: {
      myApp: { customField: "value" },
    },
  },
});

Expected Fix

Spread partCommon on the string content path (line 211):

parts: [{ ...partCommon, type: "text", text: content }]

Version

@convex-dev/agent@0.3.2

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions