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 the access-control call ac.user(user.id).workspace(id).permissions() returns no role — the caller has no role (membership) in that workspace, so permissions cannot be resolved.
Solutions
- Migrate the client to read permissions from the workspace type's permissions field (the query is deprecated).
- Ensure the user is an active member of the workspace before querying its role permissions.
- Handle space_access_denied by routing the user to the join/request-access flow instead of the workspace UI.
Defensive patterns
Strategy: validation
Validate before calling
// Ensure membership before querying role permissions
const mine = await gql.request(GET_WORKSPACES);
if (!mine.workspaces.some(w => w.id === workspaceId)) {
routeToJoinOrLanding(workspaceId);
} Type guard
const isSpaceAccessDenied = (e: unknown): boolean => getGraphqlErrorCode(e) === 'space_access_denied';
Try / catch
try {
const perms = await gql.request(WORKSPACE_ROLE_PERMISSIONS, { id: workspaceId });
} catch (e) {
if (isSpaceAccessDenied(e)) routeToJoinOrLanding(workspaceId);
} Prevention
- Migrate off the deprecated workspaceRolePermissions query to the permissions field on the workspace type.
- Gate workspace-scoped queries behind a membership check from the workspace list.
When it happens
Trigger: Querying workspaceRolePermissions(id) for a workspace the current user is not a member of (never joined, was removed, or wrong id).
Common situations: Query is deprecated in favor of WorkspaceType[permissions]; clients still on the old query hit it after losing membership; cross-workspace deep links route a non-member into the permissions query.
Related errors
AI-assisted analysis of toeverything/AFFiNE@2af30773ae (2026-08-18).
Data as JSON: /api/errors/e89cbabaa36c111c.
Report an issue: GitHub.
Appendix: 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 2af30773ae)