nocobase/nocobase · error · ResourceActionError
messages must be an array
Error message
messages must be an array
What it means
sendMessages validates that the incoming messages field is an array before reading message roles. A 400 ResourceActionError is thrown when messages is missing, null, or any non-array value (string, object, number). This guards downstream code that calls messages.find and iterates the list.
Source
Thrown at packages/plugins/@nocobase/plugin-ai/src/server/resource/aiConversations.ts:371
messages,
editingMessageId,
model,
webSearch,
stream = true,
} = ctx.action.params.values || {};
const shouldStream = stream !== false;
if (shouldStream) {
setupSSEHeaders(ctx);
}
try {
if (!sessionId) {
throw new ResourceActionError(400, ctx.t('sessionId is required'));
}
if (!Array.isArray(messages)) {
throw new ResourceActionError(400, ctx.t('messages must be an array'));
}
normalizeIncomingMessageAttachments(ctx, messages);
const userMessage = messages.find((message: any) => message.role === 'user');
if (!userMessage) {
throw new ResourceActionError(400, ctx.t('user message is required'));
}
const conversation = await plugin.aiConversationsManager.getConversation({
sessionId,
userId,
});
if (!conversation) {
throw new ResourceActionError(400, ctx.t('conversation not found'));
}
const employee = await getAIEmployee(ctx, employeeName);
if (!employee) {View on GitHub (pinned to fa42722fef)
Solutions
- Wrap the message payload in an array: messages: [{ role: 'user', content: ... }]
- Verify the request key is exactly 'messages' (not 'message') and is included in the serialized body
- If a single message, convert: const msgs = Array.isArray(m) ? m : [m]
- Check content-type is application/json so the body parses into an array rather than undefined
Example fix
// before
sendMessages({ values: { sessionId, messages: { role: 'user', content: { type: 'text', content: 'hi' } } } })
// after
sendMessages({ values: { sessionId, messages: [{ role: 'user', content: { type: 'text', content: 'hi' } }] } }) Defensive patterns
Strategy: validation
Validate before calling
function assertMessagesArray(messages) {
if (!Array.isArray(messages)) throw new TypeError('messages must be an array of message objects');
if (messages.length === 0) throw new TypeError('messages must not be empty');
} Type guard
function isMessageArray(v) {
return Array.isArray(v) && v.every((m) => m !== null && typeof m === 'object' && typeof m.role === 'string');
} Try / catch
try {
await sendMessages({ values: { sessionId, messages } });
} catch (err) {
if (err?.status === 400 && /messages must be an array/.test(err.message ?? '')) {
return sendMessages({ values: { sessionId, messages: [messages] } }); // wrap single object
}
throw err;
} Prevention
- Always wrap single messages in an array before the request
- Keep the key name exactly 'messages' in payloads and configs
- Validate payload shape client-side (zod/ajv) before calling the action
- Check the HTTP request content-type is application/json so arrays deserialize correctly
When it happens
Trigger: Calling sendMessages with values.messages undefined, with messages as a single object instead of an array (e.g. { messages: { role: 'user', ... } }), or with a JSON body where messages was serialized as null.
Common situations: Client accidentally passes one message object rather than wrapping it in an array; an SDK/HTTP layer drops the field because of a wrong content-type so it deserializes to undefined; refactored client sends 'message' (singular) key instead of 'messages'.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- sessionId is required
- user message is required
- Invalid scopeId: ${scopeId}
- `resources` must be a non-empty array
- Invalid attachment
AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01).
Data as JSON: /api/errors/f9f710705a702d10.
Report an issue: GitHub.