toeverything/AFFiNE · error · AccessDenied

access_denied

access_denied

Error message

You do not have permission to access this resource.

What it means

The artifact retrieval service authorizes the user against the workspace before running embedding match; if authorize(userId, workspaceId) fails it throws AccessDenied (code `access_denied`, status `access_denied`). This gate protects workspace-scoped artifact (embedding) search from users who are not members of the workspace.

Source

Thrown at packages/backend/server/src/plugins/copilot/retrieval/artifact.ts:35

  private async authorize(userId: string, workspaceId: string) {
    return await this.access
      .user(userId)
      .workspace(workspaceId)
      .allowLocal()
      .can('Workspace.Read');
  }

  async search(options: {
    userId: string;
    workspaceId: string;
    query: string;
    retrieval: RuntimeRetrievalScope;
    limit: number;
    messageId?: string;
    signal?: AbortSignal;
  }) {
    if (!(await this.authorize(options.userId, options.workspaceId))) {
      throw new AccessDenied();
    }
    let degraded = false;
    let matched: Awaited<ReturnType<NativeEmbeddingService['match']>> = [];
    try {
      matched = await this.embedding.match(
        options.workspaceId,
        options.query,
        'artifact',
        options.retrieval,
        options.limit,
        options.signal
      );
    } catch (error) {
      if (options.signal?.aborted) throw error;
      degraded = true;
    }
    const matchedIds = new Set(matched.map(hit => hit.artifactId));
    const missingRequired =

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Verify the user is an active member of the workspace with permission to use Copilot/retrieval
  2. Double-check the workspaceId passed to the retrieval call matches the workspace the artifacts belong to
  3. Refresh auth/session if membership was recently changed, then retry
  4. For self-hosted: inspect workspace permissions in the DB to confirm the user-workspace relation exists
Defensive patterns

Strategy: validation

Validate before calling

// guard: confirm workspace access before retrieval search
const access = await checkWorkspacePermission(userId, workspaceId); // your ACL/API check
if (!access.canRead) {
  throw new Error('user has no access to workspace — skip artifact search');
}
await artifactSearch({ userId, workspaceId, query, retrieval, limit });

Type guard

function isAccessDenied(e: unknown): boolean {
  return (e as { extensions?: { code?: string } })?.extensions?.code === 'access_denied';
}

Try / catch

try {
  await artifactSearch(params);
} catch (e) {
  if (isAccessDenied(e)) {
    disableRetrievalForWorkspace(params.workspaceId); // stop retrying with same identity
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling retrieval search with a userId that is not a member of workspaceId or lacks the required role; passing the wrong workspaceId (e.g. from another workspace's context); user removed from the workspace while their session/token is still valid.

Common situations: Cross-workspace id mixups in multi-workspace clients; permission revoked mid-session; self-hosted setups where workspace membership sync (e.g. from an identity provider) lags; tests using users never added to the workspace.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/747ce8abbc90a671. Report an issue: GitHub.