toeverything/AFFiNE · error · TooManyRequest
too_many_request
too_many_request
Error message
Too many requests.
What it means
Thrown by inviteMembers when the emails array has more than 512 entries. The mutation caps a single batch at 512 to bound work and abuse exposure; larger batches are rejected up front as too_many_requests (HTTP 429) before any email is validated.
Source
Thrown at packages/backend/server/src/core/workspaces/resolvers/member.ts:245
}));
}
}
@Mutation(() => [InviteResult])
async inviteMembers(
@CurrentUser() me: CurrentUser,
@Context() context: GraphqlContext,
@Args('workspaceId') workspaceId: string,
@Args({ name: 'emails', type: () => [String] }) emails: string[]
): Promise<InviteResult[]> {
await this.ac
.user(me.id)
.workspace(workspaceId)
.assert('Workspace.Users.Manage');
await this.assertWorkspaceNameCanInvite(workspaceId);
if (emails.length > 512) {
throw new TooManyRequest();
}
const results: InviteResult[] = emails.map(email => ({ email }));
const candidates: InviteCandidate[] = [];
const seen = new Set<string>();
for (const [index, email] of emails.entries()) {
try {
const normalizedEmail = email.trim().toLowerCase();
validators.assertValidEmail(normalizedEmail);
if (seen.has(normalizedEmail)) {
throw new ActionForbidden('Duplicate invite email.');
}
seen.add(normalizedEmail);
const target = await this.models.user.getUserByEmail(normalizedEmail);
if (target) {
const originRecord = await this.models.workspaceUser.get(
workspaceId,View on GitHub (pinned to 26c515e050)
Solutions
- Chunk emails into batches of <= 512 and call inviteMembers per chunk.
- Respect the 512 hard cap in the UI (disable the submit button above it).
- For very large imports, prefer a server-side/bulk invite job over interactive calls.
Example fix
// before
inviteMembers(workspaceId, allEmails); // allEmails.length = 2000
// after
for (const chunk of chunkArray(allEmails, 512)) {
await inviteMembers(workspaceId, chunk);
} Defensive patterns
Strategy: validation
Validate before calling
const MAX_INVITES = 512;
function chunkEmails(emails: string[], size = MAX_INVITES): string[][] {
const out: string[][] = [];
for (let i = 0; i < emails.length; i += size) out.push(emails.slice(i, i + size));
return out;
}
for (const c of chunkEmails(emails)) await inviteMembers(workspaceId, c); Type guard
function isWithinInviteLimit(emails: string[], max = 512): boolean {
return Array.isArray(emails) && emails.length <= max;
} Prevention
- Disable the invite submit button when the selected count exceeds 512.
- Chunk large lists into <= 512 batches.
- Use a server-side bulk invite job for very large imports.
When it happens
Trigger: Calling mutation inviteMembers with an emails array of length > 512.
Common situations: Bulk-import integrations that push a full contact list in one call; CSV/paste of a large distribution list; concatenating multiple selections without paging.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- member_not_found_in_space
- expect_to_revoke_doc_user_roles
- expect_to_update_doc_user_role
- action_forbidden
- query_too_long
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/fbaff62abfe3be39.
Report an issue: GitHub.