{"record":{"id":"ef5a619e99d0f923","repo":"toeverything/AFFiNE","slug":"copilot-failed-to-create-message","errorCode":"copilot_failed_to_create_message","errorMessage":"e.message","messagePattern":"e\\.message","errorType":"exception","errorClass":"CopilotFailedToCreateMessage","httpStatus":500,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/resolver.ts","lineNumber":781,"sourceCode":"\n  @Mutation(() => String, {\n    description: 'Create a chat message',\n  })\n  @CallMetric('ai', 'chat_message_create')\n  async createCopilotMessage(\n    @CurrentUser() user: CurrentUser,\n    @Args({ name: 'options', type: () => CreateChatMessageInput })\n    options: CreateChatMessageInput\n  ): Promise<string> {\n    const lockFlag = `${COPILOT_LOCKER}:message:${user?.id}:${options.sessionId}`;\n    await using lock = await this.mutex.acquire(lockFlag);\n    if (!lock) {\n      throw new TooManyRequest('Server is busy');\n    }\n    try {\n      return await this.inbox.createMessage(user.id, options);\n    } catch (e: any) {\n      throw new CopilotFailedToCreateMessage(e.message);\n    }\n  }\n\n  private transformToSessionType(session: Omit<ChatHistory, 'messages'>) {\n    return { id: session.sessionId, ...session };\n  }\n}\n\n@Throttle()\n@CopilotEnabled()\n@Resolver(() => UserType)\nexport class UserCopilotResolver {\n  constructor(private readonly ac: PermissionAccess) {}\n\n  @ResolveField(() => CopilotType)\n  async copilot(\n    @CurrentUser() user: CurrentUser,\n    @Args('workspaceId', { nullable: true }) workspaceId?: string","sourceCodeStart":763,"sourceCodeEnd":799,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/resolver.ts#L763-L799","documentation":"createCopilotMessage wraps ANY failure of inbox.createMessage into CopilotFailedToCreateMessage (code `copilot_failed_to_create_message`, status `internal_server_error`), forwarding the original e.message. It is a wrapper error: the actionable cause is the message text carried inside, which comes from the inbox pipeline (session lookup, params validation, attachments, downstream providers).","triggerScenarios":"inbox.createMessage throws: the sessionId no longer exists, message params/attachments fail validation, the workspace's AI provider or DB rejects the request, or a downstream prompt/runtime error occurred while enqueueing the message.","commonSituations":"Sending into a session deleted by cleanup; oversized or malformed attachments; provider credentials/quota issues surfacing at message-create time; custom inbox code paths that throw plain Errors.","solutions":["Read the forwarded message — it names the underlying cause thrown by inbox.createMessage","Verify the sessionId still exists (query copilot session history) before sending","Check the attachment/params payload for size, format, and required fields","Correlate with server logs for the original stack of the wrapped error (the wrapper hides trace location)"],"exampleFix":"// before\ntry {\n  await createCopilotMessage({ variables: { options } });\n} catch (e) {\n  toast('Failed'); // underlying cause lost\n}\n\n// after\ntry {\n  await createCopilotMessage({ variables: { options } });\n} catch (e) {\n  const cause = (e as GraphQLError)?.extensions?.code === 'copilot_failed_to_create_message'\n    ? (e as Error).message // server forwarded inbox error text\n    : 'Failed to send';\n  toast(cause);\n}","handlingStrategy":"try-catch","validationCode":"// guard: verify the session exists and the payload is sane before sending\nconst sessions = await fetchCopilotSessions(workspaceId);\nif (!sessions.some(s => s.id === options.sessionId)) {\n  throw new Error('session missing — recreate before sending');\n}\nif (options.attachments?.some(a => a.size > MAX_ATTACHMENT_BYTES)) {\n  throw new Error('attachment too large');\n}","typeGuard":"function isFailedToCreateMessage(e: unknown): boolean {\n  return (e as { extensions?: { code?: string } })?.extensions?.code === 'copilot_failed_to_create_message';\n}","tryCatchPattern":"try {\n  return await createCopilotMessage({ variables: { options } });\n} catch (e) {\n  if (isFailedToCreateMessage(e)) {\n    // e.message carries the forwarded inbox error — surface it, do not blind-retry\n    reportToUser((e as Error).message);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Check the session exists before sending if it may have been cleaned up","Validate attachment size/format client-side before the mutation","Always log the forwarded inner message — the wrapper alone says nothing actionable"],"tags":["copilot","chat-message","wrapper-error","internal-error","graphql"],"backgroundTag":"wrapped-upstream-error","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}