toeverything/AFFiNE · warning · TooManyRequest

too_many_request

too_many_request

Error message

Server is busy

What it means

Thrown by the copilot chat-session create mutation when the per-user, per-workspace mutex (lock key `copilot:session:{userId}:{workspaceId}`) cannot be acquired. AFFiNE serializes all chat-session mutations for one user+workspace so concurrent creates cannot interleave; acquisition is non-blocking, and failure immediately raises TooManyRequest (code `too_many_request`, status `too_many_requests`) with the message 'Server is busy'. It signals lock contention with an in-flight mutation, not global server load.

Source

Thrown at packages/backend/server/src/plugins/copilot/resolver.ts:609

        ) as ChatMessageType[],
      })),
      'updatedAt',
      pagination,
      totalCount
    );
  }

  private async createCopilotSessionInternal(
    user: CurrentUser,
    options: CreateChatSessionInput
  ): Promise<string> {
    // permission check based on session type
    await this.assertPermission(user, options);

    const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`;
    await using lock = await this.mutex.acquire(lockFlag);
    if (!lock) {
      throw new TooManyRequest('Server is busy');
    }

    return await this.chatSession.create({
      ...options,
      pinned: options.pinned ?? false,
      docId: options.docId ?? null,
      userId: user.id,
    });
  }

  @Mutation(() => String, {
    description: 'Create a chat session',
    deprecationReason: 'use `createCopilotSessionWithHistory` instead',
  })
  @CallMetric('ai', 'chat_session_create')
  async createCopilotSession(
    @CurrentUser() user: CurrentUser,
    @Args({ name: 'options', type: () => CreateChatSessionInput })

View on GitHub (pinned to b4c8548c09)

Solutions

  1. De-duplicate submissions client-side: disable the button / set an in-flight flag until the first createCopilotSession resolves
  2. Retry once after the in-flight mutation completes — the lock is auto-released (`await using`) when the first request finishes
  3. Verify only one component instance mounts and calls the mutation for the same workspace
  4. If parallel calls are intentional, queue them sequentially instead of firing them concurrently

Example fix

// before
await createCopilotSession({ variables: { options } }); // fired twice on double click

// after
const creating = useRef(false);
async function handleCreate() {
  if (creating.current) return; // skip duplicate submit
  creating.current = true;
  try {
    await createCopilotSession({ variables: { options } });
  } finally {
    creating.current = false;
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// guard: skip duplicate create while one is in flight
const inflightCreate = useRef<Promise<string> | null>(null);
function createSession(options: CreateChatSessionInput) {
  inflightCreate.current ??= createCopilotSession({ variables: { options } })
    .finally(() => (inflightCreate.current = null));
  return inflightCreate.current;
}

Type guard

function isServerBusy(e: unknown): boolean {
  return (
    !!e && typeof e === 'object' &&
    (e as { extensions?: { code?: string } }).extensions?.code === 'too_many_request'
  );
}

Try / catch

try {
  await createCopilotSession({ variables: { options } });
} catch (e) {
  if (isServerBusy(e)) {
    await waitForInflightMutations(); // lock is released when the other request ends
    return createCopilotSession({ variables: { options } }); // single retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling createCopilotSession / createCopilotSessionWithHistory while another session mutation (create, update, fork, cleanup) for the SAME user.id + workspaceId still holds the lock — e.g. double-clicking 'New chat', firing the mutation from two tabs, or retrying before the first request finished.

Common situations: Duplicate form submission in the frontend; React StrictMode double-invoking an effect that sends the mutation; a slow create request still being awaited while the UI or a retry helper fires again; test suites issuing parallel creates for one user/workspace.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/f5d780b953681c82. Report an issue: GitHub.