toeverything/AFFiNE · warning · NoMoreSeat

no_more_seat

no_more_seat

Error message

No more seat available in the Space ${spaceId}.

What it means

Thrown inside inviteMembers (non-team workspace) when admitting one more seat would exceed the workspace seat quota: quota.memberCount + already-successful-this-batch + 1 > quota.memberLimit. Team workspaces skip this (they go through allocating-seat path); non-team workspaces enforce the cap per candidate. Reported as no_more_seat (bad_request, HTTP 400).

Source

Thrown at packages/backend/server/src/core/workspaces/resolvers/member.ts:334

              email: candidate.normalizedEmail,
              registered: false,
            });
          }

          const existingMember = await this.models.workspaceUser.get(
            workspaceId,
            target.id
          );
          if (existingMember) {
            throw new AlreadyInSpace({ spaceId: workspaceId });
          }

          if (!isTeam) {
            const needMoreSeat =
              quota.memberCount + successfulCandidates.length + 1 >
              quota.memberLimit;
            if (needMoreSeat) {
              throw new NoMoreSeat({ spaceId: workspaceId });
            }
          }

          // no need to check quota, directly go allocating seat path
          if (isTeam) {
            const role = await this.models.workspaceUser.set(
              workspaceId,
              target.id,
              WorkspaceRole.Collaborator,
              {
                status: WorkspaceMemberStatus.AllocatingSeat,
                source: WorkspaceMemberSource.Email,
                inviterId: me.id,
              }
            );
            await this.allocateAvailableTeamSeats(
              workspaceId,
              quota.memberLimit

View on GitHub (pinned to 26c515e050)

Solutions

  1. Upgrade the workspace plan / increase memberLimit to fit the new seats.
  2. Reduce the batch size to the remaining available seats (memberLimit - memberCount).
  3. Remove unused/pending members to free seats before inviting.
  4. Refresh quota.getWorkspaceSeatQuota in the UI before enabling invite.

Example fix

// before
inviteMembers(workspaceId, manyEmails); // exceeds seats
// after
const free = quota.memberLimit - quota.memberCount;
inviteMembers(workspaceId, manyEmails.slice(0, Math.max(0, free)));
Defensive patterns

Strategy: validation

Validate before calling

const quota = await getWorkspaceSeatQuota(workspaceId);
const free = Math.max(0, quota.memberLimit - quota.memberCount);
const toInvite = emails.slice(0, free);
if (toInvite.length === 0) throw new Error('No seats available; upgrade plan to invite more');
await inviteMembers(workspaceId, toInvite);

Type guard

function hasFreeSeats(quota: { memberCount: number; memberLimit: number }): boolean {
  return quota.memberCount < quota.memberLimit;
}

Prevention

When it happens

Trigger: Inviting to a non-team workspace when the number of members plus pending invites in this batch would exceed memberLimit.

Common situations: Plan limit reached and the user tries to invite more; a batch where earlier candidates consumed the last seats; stale quota shown in the UI that no longer reflects reality.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/907e929ce543442f. Report an issue: GitHub.