toeverything/AFFiNE · error · SpaceNotFound

space_not_found

space_not_found

Error message

Space ${spaceId} not found.

What it means

Thrown by the `workspace` query when `models.workspace.get(id)` returns null after the `Workspace.Read` permission assertion already passed. Indicates the id is known to the permission/access-control layer but no workspace record exists in the database. Coded `space_not_found` (resource_not_found) with `{ spaceId }`.

Source

Thrown at packages/backend/server/src/core/workspaces/resolvers/workspace.ts:176

    );

    return workspaces.map(workspace => ({
      ...workspace,
      permission: map.get(workspace.id),
      role: map.get(workspace.id),
    }));
  }

  @Query(() => WorkspaceType, {
    description: 'Get workspace by id',
  })
  async workspace(@CurrentUser() user: CurrentUser, @Args('id') id: string) {
    await this.ac.user(user.id).workspace(id).assert('Workspace.Read');

    const workspace = await this.models.workspace.get(id);

    if (!workspace) {
      throw new SpaceNotFound({ spaceId: id });
    }

    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();

View on GitHub (pinned to 26c515e050)

Solutions

  1. Clear the client's cached workspace id and redirect the user to their workspace list.
  2. Confirm the id is correct and was not deleted by an admin.
  3. If you operate the deployment, verify the `workspace` table row exists and reconcile the permission entries if missing.
  4. Treat the error as a hard 404 and stop retrying — the workspace will not reappear on its own.

Example fix

// before
const ws = await sdk.workspace({ id });

// after
try {
  const ws = await sdk.workspace({ id });
} catch (e) {
  if (e.code === 'space_not_found') {
    clearCachedWorkspaceId(id);
    router.push('/workspaces');
    return;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the workspace id is still present in the user's list
const mine = await sdk.getMyWorkspaces();
if (!mine.find(w => w.id === id)) {
  router.push('/workspaces');
  return;
}

Type guard

function isKnownWorkspace(id, myWorkspaces) {
  return myWorkspaces.some(w => w.id === id);
}

Try / catch

try {
  const ws = await sdk.workspace({ id });
} catch (e) {
  if (e.code === 'space_not_found') {
    clearCachedWorkspaceId(id);
    router.push('/workspaces');
  } else throw e;
}

Prevention

When it happens

Trigger: Querying `workspace(id)` with an id that was deleted, was never created, is stale (cached client-side after deletion), or for which the DB row is missing while the permission system still has an entry. Rare in practice because the permission check usually fails first for non-members.

Common situations: Client holds a workspace id from a previous session after the workspace was deleted; a partially-failed workspace creation left permission entries but no workspace row; id typo or copy error; cross-shard inconsistency.

Related errors


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