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 deprecated `workspaceRolePermissions` query when `ac.user(user.id).workspace(id).permissions()` returns no role for the caller — i.e. the user has no membership or role assignment in that workspace. Coded `space_access_denied` (no_permission) with `{ spaceId }`.
Source
Thrown at packages/backend/server/src/core/workspaces/resolvers/workspace.ts:196
return workspace;
}
@Query(() => WorkspaceRolePermissions, {
description: 'Get workspace role permissions',
deprecationReason: 'use WorkspaceType[permissions] instead',
})
async workspaceRolePermissions(
@CurrentUser() user: CurrentUser,
@Args('id') id: string
): Promise<WorkspaceRolePermissions> {
const { role, permissions } = await this.ac
.user(user.id)
.workspace(id)
.permissions();
if (!role) {
throw new SpaceAccessDenied({ spaceId: id });
}
return {
role,
permissions: mapPermissionsToGraphqlPermissions(permissions),
};
}
@Mutation(() => WorkspaceType, {
description: 'Create a new workspace',
})
async createWorkspace(
@CurrentUser() user: CurrentUser,
// we no longer support init workspace with a preload file
// use sync system to uploading them once created
@Args({ name: 'init', type: () => GraphQLUpload, nullable: true })
init: FileUpload | null
) {View on GitHub (pinned to 26c515e050)
Solutions
- Confirm the user is still a member of the workspace before querying role permissions.
- Prefer the non-deprecated `WorkspaceType[permissions]` field noted in the deprecation reason.
- Handle `space_access_denied` by redirecting to the workspace list or a 'no access' page.
- Refresh the session/membership cache if the role was just granted and is not yet visible.
Example fix
// before
const { role, permissions } = await sdk.workspaceRolePermissions({ id });
// after
const ws = await sdk.workspace({ id }); // WorkspaceType.permissions is the replacement
const permissions = ws.permissions;
// guard:
try { ... } catch (e) {
if (e.code === 'space_access_denied') router.push('/workspaces');
} Defensive patterns
Strategy: validation
Validate before calling
// Prefer the non-deprecated path and confirm membership
if (!myWorkspaces.find(w => w.id === workspaceId)) {
router.push('/workspaces');
return;
}
const ws = await sdk.workspace({ id: workspaceId });
const permissions = ws.permissions; Type guard
function hasWorkspaceAccess(workspaceId, myWorkspaces) {
return myWorkspaces.some(w => w.id === workspaceId);
} Try / catch
try {
const { role, permissions } = await sdk.workspaceRolePermissions({ id });
} catch (e) {
if (e.code === 'space_access_denied') {
router.push('/workspaces');
} else throw e;
} Prevention
- Migrate off the deprecated query to WorkspaceType.permissions.
- Confirm membership before querying role permissions.
- Handle space_access_denied by redirecting to the workspace list.
When it happens
Trigger: Calling `workspaceRolePermissions(workspaceId)` as a user who is not a member of the workspace — no role row in `workspace_user`, no inherited permission. Also fires after a user is removed but their client still issues the query.
Common situations: User removed from the workspace but the page still mounted; a shared URL opened by an outsider; permission system hasn't yet propagated a freshly-revoked role; querying a workspace the user was never invited to.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/e89cbabaa36c111c.
Report an issue: GitHub.