{"record":{"id":"b6a56c6c3bae4c5a","repo":"nocobase/nocobase","slug":"message-not-found","errorCode":null,"errorMessage":"message not found","messagePattern":"message not found","errorType":"validation","errorClass":"ResourceActionError","httpStatus":400,"severity":"error","filePath":"packages/plugins/@nocobase/plugin-ai/src/server/resource/aiConversations.ts","lineNumber":614,"sourceCode":"        if (!conversation) {\n          throw new ResourceActionError(400, ctx.t('conversation not found'));\n        }\n\n        const employee = await getAIEmployee(ctx, conversation.aiEmployeeUsername);\n        if (!employee) {\n          throw new ResourceActionError(400, ctx.t('AI employee not found'));\n        }\n\n        const resendMessages: AIMessageInput[] = [];\n        if (messageId) {\n          const message = await ctx.db.getRepository('aiConversations.messages', sessionId).findOne({\n            filter: {\n              messageId,\n            },\n          });\n\n          if (!message) {\n            throw new ResourceActionError(400, ctx.t('message not found'));\n          }\n        } else {\n          const message = await ctx.db.getRepository('aiConversations.messages', sessionId).findOne({\n            filter: {\n              sessionId,\n            },\n            sort: ['-messageId'],\n          });\n          if (!message) {\n            throw new ResourceActionError(400, ctx.t('message not found'));\n          }\n          messageId = message.messageId;\n          if (['user', 'tool'].includes(message.role)) {\n            resendMessages.push({\n              role: message.role,\n              content: message.content,\n              toolCalls: message.toolCalls,\n              attachments: message.attachments,","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/plugins/@nocobase/plugin-ai/src/server/resource/aiConversations.ts#L596-L632","documentation":"resendMessages needs a message to resend: if a messageId was provided it loads that message from the aiConversations.messages repository, otherwise it takes the latest message of the session. It throws this 400 ResourceActionError when no message row matches the given messageId, or (in the no-messageId branch) when the conversation has no messages at all.","triggerScenarios":"resendMessages called with values.messageId that was never persisted (client-generated id, message from a different session, or already deleted); or without messageId on a conversation whose message list is empty; message written to the wrong session bucket so the filter { messageId } misses.","commonSituations":"Client keeps a messageId after the message was deleted server-side; passing the local optimistic-UI id instead of the server-assigned messageId; calling resend on a brand-new conversation with zero messages; running against a reset database where old message ids no longer exist.","solutions":["Confirm the messageId exists in the aiConversations.messages table for that sessionId; use the server-assigned id, not a client-generated one","Omit messageId to let the server pick the latest message of the conversation","If the conversation is empty, send a new message via sendMessages instead of resending","Ensure the message belongs to this sessionId (repository is namespaced per session)"],"exampleFix":"// before\nawait resource.resendMessages({ values: { sessionId, messageId: localTempId } }); // client-side id\n// after\nconst saved = await resource('aiConversations.messages', sessionId).list();\nawait resource.resendMessages({ values: { sessionId, messageId: saved.data[0].messageId } });","handlingStrategy":"validation","validationCode":"async function assertMessageResendable(sessionId, messageId) {\n  const repo = resource('aiConversations.messages', sessionId);\n  const res = await repo.list({ filter: messageId ? { messageId } : { sessionId }, sort: ['-messageId'], page: 1, pageSize: 1 });\n  if (!res?.data?.length) throw new Error('No message available to resend in this conversation');\n  return res.data[0].messageId;\n}\nconst safeMessageId = await assertMessageResendable(sessionId, messageId);","typeGuard":"function isStoredMessage(msg) {\n  return typeof msg === 'object' && msg !== null && typeof msg.messageId === 'string' && typeof msg.role === 'string';\n}","tryCatchPattern":"try {\n  await resource.resendMessages({ values: { sessionId, messageId } });\n} catch (err) {\n  if (err?.status === 400 && /message not found/.test(err.message ?? '')) {\n    return resource.resendMessages({ values: { sessionId } }); // let server pick latest\n  }\n  throw err;\n}","preventionTips":["Always use server-assigned messageIds, never client-generated temp ids","Scope message lookups to the correct session bucket","If the conversation has no messages, use sendMessages rather than resendMessages","Refresh the visible message list after deletes so stale ids are not resent"],"tags":["not-found","message-format","request-params","ai-conversations"],"backgroundTag":"resource-not-found","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}