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

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