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
- De-duplicate submissions client-side: disable the button / set an in-flight flag until the first createCopilotSession resolves
- Retry once after the in-flight mutation completes — the lock is auto-released (`await using`) when the first request finishes
- Verify only one component instance mounts and calls the mutation for the same workspace
- 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
- One in-flight session mutation per user+workspace in the UI; disable the action while pending
- Serialize session mutations through a per-workspace promise queue
- Never fire the create mutation from effects that can double-run (StrictMode) without an in-flight guard
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
- too_many_request
- copilot_session_not_found
- invalid_license_to_activate
- comment_not_found
- internal_server_error
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/f5d780b953681c82.
Report an issue: GitHub.