toeverything/AFFiNE · error · TooManyRequest
too_many_request
too_many_request
Error message
Too many requests.
What it means
Thrown by InviteQuotaAssertService.assertWorkspaceInviteQuota when the external invite-quota runtime (runtime.assertWorkspaceInviteQuotaV1) throws. The catch block is fail-closed by default: unless config.auth.inviteQuotaFailOpenOnRuntimeError is true, the server rethrows TooManyRequest (HTTP 429, code too_many_request) rather than let a broken limiter silently admit invite spam. A genuine quota rejection on the allowed path also surfaces under the same code, so the server log line 'Workspace invite quota native assert failed' is what distinguishes this infra-failure variant.
Solutions
- Grep server logs for 'Workspace invite quota native assert failed' — its error: field names the real runtime failure (auth, DNS, timeout)
- Fix or provision the quota runtime backend so assertWorkspaceInviteQuotaV1 succeeds
- Self-host: set auth.inviteQuotaFailOpenOnRuntimeError=true so an unavailable limiter degrades to allow instead of 429
- Retry the invite once the underlying issue is resolved; watch the invite_quota_runtime_fallback metric to confirm the mode
Defensive patterns
Strategy: retry
Try / catch
let lastErr;
for (const delay of [1000, 5000, 15000]) {
try {
return await inviteMembers(wsId, emails);
} catch (e) {
if (e?.code === 'too_many_request' && serverLogsShowRuntimeFallback()) {
lastErr = e;
await sleep(delay); // infra fail-closed, not user abuse — safe to retry
continue;
}
throw e;
}
}
throw lastErr; Prevention
- Monitor the invite_quota_runtime_fallback metric; sustained fail_closed mode means the quota backend is down
- Self-host: set auth.inviteQuotaFailOpenOnRuntimeError=true if you accept open invites when the limiter is unavailable
- Provision and health-check the invite-quota runtime backend before enabling the feature
- Surface a distinct UI message for this 429 ('invite service unavailable, retrying') instead of 'you are rate limited'
When it happens
Trigger: Calling workspace invite mutations while the quota runtime errors: connectivity failure to the abuse-control backend, missing/misconfigured runtime credentials, or a malformed decision payload — with failOpen disabled, every such runtime failure becomes 429 for the inviter.
Common situations: Self-hosted deployments that enabled the invite abuse runtime without provisioning its backend; transient network partitions between app server and quota service; cloud config copied to an environment lacking the cloud infrastructure; misreading this 429 as user-level rate limiting when it is actually fail-closed infra.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b6de0ad51b (2026-08-18).
Data as JSON: /api/errors/019a1deb3a8c9bcf.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/workspaces/abuse.ts:290
? 'fail_open'
: 'fail_closed',
});
this.logger.error('Workspace invite quota native assert failed', {
userId: input.actorUserId,
workspaceId: input.workspaceId,
targetCount: input.targetCount,
targetDomainsSummary: input.targetDomains,
sourceTrusted: input.source?.trusted ?? false,
country: input.source?.country,
asn: input.source?.asn,
requestId: input.requestId,
cfRay: input.source?.rayId,
error: message,
});
if (this.config.auth.inviteQuotaFailOpenOnRuntimeError) {
return { decision: { allowed: true, requested: input.targetCount } };
}
throw new TooManyRequest();
}
metrics.workspace
.counter('invite_quota_requested_targets')
.add(input.targetCount);
metrics.workspace
.histogram('invite_quota_counter_latency_ms')
.record(Date.now() - start);
if (!decision.allowed) {
metrics.workspace.counter('invite_quota_rejected').add(1, {
reason: decision.reason ?? 'unknown',
});
metrics.workspace.counter('invite_quota_reject_by_reason').add(1, {
reason: decision.reason ?? 'unknown',
});
if (this.config.auth.inviteQuotaShadowMode) {
this.logger.warn('Workspace invite quota shadow rejected', {
userId: input.actorUserId,View on GitHub (pinned to b6de0ad51b)