toeverything/AFFiNE · error · SpaceAccessDenied

space_access_denied

space_access_denied

Error message

You do not have permission to access Space ${spaceId}.

What it means

Thrown by the private `assertWorkspaceAcceptsMemberChange` helper when `policy.getWorkspaceState(workspaceId)` reports the workspace as readonly (`isReadonly`). A readonly workspace blocks all membership mutations (accept invite, etc.). Coded `space_access_denied` (no_permission) with `{ spaceId }`. It guards both `acceptInvitationByEmail` and `acceptInvitationByLink`.

Source

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

      workspaceId,
      user.id,
      WorkspaceRole.Collaborator,
      {
        status: WorkspaceMemberStatus.UnderReview,
        source: WorkspaceMemberSource.Link,
        inviterId: inviter.id,
      }
    );

    await this.workspaceService.sendReviewRequestNotification(role.id);
    this.event.emit('workspace.members.updated', { workspaceId });
    return;
  }

  private async assertWorkspaceAcceptsMemberChange(workspaceId: string) {
    const state = await this.policy.getWorkspaceState(workspaceId);
    if (state.isReadonly) {
      throw new SpaceAccessDenied({ spaceId: workspaceId });
    }
  }

  private async allocateAvailableTeamSeats(workspaceId: string, limit: number) {
    if (limit <= 0) return;
    await this.workspaceService.allocateSeats(workspaceId, limit);
  }
}

View on GitHub (pinned to 26c515e050)

Solutions

  1. Restore the workspace's subscription / billing so the policy state flips out of readonly.
  2. Contact the workspace owner or platform support to lift the readonly/frozen state, then retry the invite.
  3. Check `getWorkspaceState` to confirm `isReadonly` before retrying acceptance.
  4. Do not retry in a tight loop — readonly state is only cleared by an external action (payment/support).

Example fix

// before
await sdk.acceptInviteById({ inviteId });

// after
const state = await sdk.getWorkspaceState({ workspaceId });
if (state.isReadonly) {
  notify('This workspace is currently locked. Ask the owner to restore the subscription.');
  return;
}
await sdk.acceptInviteById({ inviteId });
Defensive patterns

Strategy: try-catch

Validate before calling

// Check workspace state before accepting
const state = await sdk.getWorkspaceState({ workspaceId });
if (state.isReadonly) {
  notify('This workspace is locked. Ask the owner to restore the subscription.');
  return;
}

Type guard

function isWorkspaceLocked(state) {
  return Boolean(state && state.isReadonly);
}

Try / catch

try {
  await sdk.acceptInviteById({ inviteId });
} catch (e) {
  if (e.code === 'space_access_denied') {
    notify('This workspace is currently locked. Please try again later.');
  } else throw e;
}

Prevention

When it happens

Trigger: Accepting any invitation to a workspace whose policy state is locked — e.g. subscription lapsed/billing failure, the workspace was suspended for abuse, or an admin manually froze the workspace.

Common situations: Workspace on a plan whose billing failed and entered a grace/readonly period; a workspace suspended by the platform; trial expired into a read-only tier; an internal freeze flag flipped by ops.

Related errors


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