toeverything/AFFiNE · error · CopilotQuotaExceeded
copilot_quota_exceeded
copilot_quota_exceeded
Error message
You have reached the limit of actions in this workspace, please upgrade your plan.
What it means
CopilotQuotaExceeded (code copilot_quota_exceeded) is thrown by CopilotPolicy.checkQuota when the user's consumed message count (models.copilotSession.countUserMessages) has reached quota.copilotActionLimit from their plan. Only finite limits trigger it: limit undefined or Infinity means unlimited. It gates message creation, so the user must upgrade or wait for the limit window to reset.
Source
Thrown at packages/backend/server/src/plugins/copilot/conversation/policy.ts:27
@Injectable()
export class ConversationPolicy {
constructor(
private readonly models: Models,
private readonly quota: QuotaService
) {}
async getQuota(userId: string) {
const quota = await this.quota.getUserQuota(userId);
const limit = quota.copilotActionLimit;
const used = await this.models.copilotSession.countUserMessages(userId);
return { limit, used };
}
async checkQuota(userId: string) {
if (!(await this.hasQuota(userId))) {
throw new CopilotQuotaExceeded();
}
}
async hasQuota(userId: string) {
const { limit, used } = await this.getQuota(userId);
return !(limit !== undefined && Number.isFinite(limit) && used >= limit);
}
shouldScheduleTitle(prompt: Pick<ResolvedPrompt, 'action'>) {
return !prompt.action;
}
shouldGenerateTitle(input: { title: string | null; turns: Turn[] }) {
if (input.title || !input.turns.length) {
return false;
}
let hasUser = false;View on GitHub (pinned to b4c8548c09)
Solutions
- Call getQuota(userId) before sending and stop or prompt for upgrade when used >= limit
- Upgrade the user's plan to raise copilotActionLimit
- Wait for the quota window reset (usage is counted per period) if the plan is periodic
- As an admin: verify quota.copilotActionLimit is configured as intended for the user's plan tier
Example fix
// before
await inbox.createMessage(userId, options); // throws copilot_quota_exceeded
// after
const { limit, used } = await policy.getQuota(userId);
if (limit !== undefined && Number.isFinite(limit) && used >= limit) {
return promptUpgradePlan();
}
await inbox.createMessage(userId, options); Defensive patterns
Strategy: validation
Validate before calling
const { limit, used } = await policy.getQuota(userId);
const hasQuota = !(limit !== undefined && Number.isFinite(limit) && used >= limit);
if (!hasQuota) {
return showUpgradePrompt({ limit, used });
} Try / catch
try {
await inbox.createMessage(userId, options);
} catch (e) {
if (e.code === 'copilot_quota_exceeded') {
const { limit, used } = await policy.getQuota(userId);
showUpgradePrompt({ limit, used });
} else throw e;
} Prevention
- Check quota before each send and show remaining count in the UI
- Batch/queue drafts locally when near the limit instead of firing failing requests
- Gate automated scripts on getQuota so they stop before exhausting the plan
When it happens
Trigger: Calling createMessage (or any flow that runs checkQuota) after used >= limit on a free/capped plan; limits changed on an active user dropping limit below current usage; heavy automated scripting that posts many messages.
Common situations: Free-tier users automate copilot calls and hit the cap mid-task; a plan downgrade applies a lower limit than already consumed; QA load tests exhaust a shared account quota.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/5555bb33144b06ac.
Report an issue: GitHub.