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.

Solutions

  1. Call getQuota(userId) before sending and stop or prompt for upgrade when used >= limit
  2. Upgrade the user's plan to raise copilotActionLimit
  3. Wait for the quota window reset (usage is counted per period) if the plan is periodic
  4. 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

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@2af30773ae (2026-08-18). Data as JSON: /api/errors/5555bb33144b06ac. Report an issue: GitHub.

Appendix: 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 2af30773ae)