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
- Restore the workspace's subscription / billing so the policy state flips out of readonly.
- Contact the workspace owner or platform support to lift the readonly/frozen state, then retry the invite.
- Check `getWorkspaceState` to confirm `isReadonly` before retrying acceptance.
- 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
- Keep workspace billing active to avoid the readonly state.
- Surface locked state in the UI early via getWorkspaceState.
- Do not retry automatically — readonly clears only via an external action.
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
- no_more_seat
- action_forbidden_on_non_team_workspace
- can_not_revoke_yourself
- invalid_invitation
- authentication_required
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/eb7485d51aa126fd.
Report an issue: GitHub.