toeverything/AFFiNE · error · SpaceAccessDenied
space_access_denied
space_access_denied
Error message
You do not have permission to access Space ${spaceId}. What it means
IndexerService.unwrap translates search-operation results for the caller. An output with errorCode 'workspace_denied' is mapped to SpaceAccessDenied (code space_access_denied) with the queried workspaceId: the permission check concluded the current user has no access to that workspace.
Source
Thrown at packages/backend/server/src/plugins/indexer/service.ts:160
)
);
for (const doc of docs) {
if (!doc.title) doc.title = titles.get(doc.docId) ?? '';
}
}
const users = await this.models.user.getPublicUsersMap(userIds);
for (const doc of docs) {
doc.createdByUser = users.get(doc.createdByUserId);
doc.updatedByUser = users.get(doc.updatedByUserId);
}
return docs;
}
private unwrap<T>(output: SearchOperationOutput, workspaceId: string): T {
if (output.ok) return output.value as T;
switch (output.errorCode) {
case 'workspace_denied':
throw new SpaceAccessDenied({ spaceId: workspaceId });
case 'invalid_request':
case 'unsupported_query':
throw new InvalidIndexerInput({ reason: output.errorCode });
case 'provider_unavailable':
throw new SearchProviderUnavailable();
case 'index_not_ready':
throw new SearchIndexNotReady({ spaceId: workspaceId });
case 'permission_syncing':
throw new SearchPermissionSyncing();
case 'index_failed':
throw new SearchIndexFailed({
diagnosticId: 'search_workspace_reconcile_failed',
});
default:
throw new InternalServerError();
}
}
}View on GitHub (pinned to b6de0ad51b)
Solutions
- Verify the caller's membership/permission for workspaceId before invoking search
- Refresh the session/token and retry to rule out a stale auth context
- Check that workspaceId in the query is actually the workspace the client has open
Example fix
// before
const docs = await indexerService.search(user, { workspaceId, query });
// after: gate on permission first
const allowed = await permissionService.tryCheck(workspaceId, user.id);
if (!allowed) throw new SpaceAccessDenied({ spaceId: workspaceId });
const docs = await indexerService.search(user, { workspaceId, query }); Defensive patterns
Strategy: validation
Validate before calling
// check membership before calling indexer search
const permission = await permissionService.get(workspaceId, user.id);
if (!permission || !permission.canRead) {
return forbidden(spaceId);
}
const docs = await indexerService.search(user, { workspaceId, query }); Try / catch
// distinguish denial from provider/permission availability
try {
return await indexerService.search(user, { workspaceId, query });
} catch (e) {
if (e instanceof SpaceAccessDenied) { // code === 'space_access_denied'
return fortyThree({ spaceId: e.spaceId }); // 403, do not retry
}
throw e; // provider/permission-unavailable handled separately
} Prevention
- Run the permission check in the same request that opens a workspace, and reuse its result for search
- Re-check permissions on focus/resume — membership may have been revoked while the client was open
- Never forward a workspaceId taken from client input without scoping it to the session's accessible workspaces
When it happens
Trigger: Calling an indexer search for a workspaceId the requesting user is not a member of, or with a session/token that belongs to a different user; also stale permissions after the user's membership was revoked mid-session.
Common situations: Expired or mismatched auth context attached to the search call; permissions changed while a client kept a workspace open; passing another workspace's id in the query (e.g. stale route param); invoking admin/indexer endpoints with a regular user token.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- workspace_permission_not_found
- invalid_indexer_input
- search_provider_not_found
- internal_server_error
- access_denied
AI-assisted analysis of toeverything/AFFiNE@b6de0ad51b (2026-08-21).
Data as JSON: /api/errors/f95ee2e1dfd718df.
Report an issue: GitHub.