toeverything/AFFiNE · error · ActionForbidden

action_forbidden

action_forbidden

Error message

This feature is temporarily unavailable for you.

What it means

publishDoc runs assertCanShare before publishing. If the acting user is currently quarantined or banned by the invite-abuse system, the action is refused with ActionForbidden and a deliberately generic message. This is an anti-abuse gate, not a permissions bug: the user may hold Doc.Publish on the workspace and still be blocked from share actions.

Solutions

  1. Appeal the flag or contact support to have the quarantine lifted for the account
  2. If the quarantine is time-based, wait for it to expire and retry
  3. Publish from a different, unflagged account that has Doc.Publish rights on the workspace
Defensive patterns

Strategy: try-catch

Type guard

function isActionForbidden(e: unknown): boolean {
  return (
    typeof e === 'object' && e !== null &&
    (e as { extensions?: { code?: string } }).extensions?.code === 'action_forbidden'
  );
}

Try / catch

try {
  await publishDoc(wsId, docId, mode);
} catch (e) {
  if (isActionForbidden(e)) {
    showRestrictedMessage('Sharing is temporarily unavailable for this account.'); // do not retry
  } else throw e;
}

Prevention

When it happens

Trigger: A publishDoc mutation executed by an account flagged by the invite-abuse heuristics (mass invites, spam-like behavior). The server logs 'Share action blocked for quarantined actor' with the userId and context.

Common situations: Cloud-hosted AFFiNE accounts that tripped abuse detection; automation/scripts issuing many invite or share operations that resemble spam.

Related errors


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

Appendix: source

Thrown at packages/backend/server/src/core/workspaces/resolvers/doc.ts:332

    private readonly ac: PermissionAccess,
    private readonly models: Models,
    private readonly cache: Cache,
    private readonly event: EventBus,
    private readonly runtime: BackendRuntimeProvider
  ) {}

  @ResolveField(() => WorkspaceDocMeta, {
    description: 'Cloud page metadata of workspace',
    complexity: 2,
    deprecationReason: 'use [WorkspaceType.doc] instead',
  })
  async pageMeta(
    @CurrentUser() me: CurrentUser,
    @Parent() workspace: WorkspaceType,
    @Args('pageId') pageId: string
  ) {
    await this.ac.user(me.id).doc(workspace.id, pageId).assert('Doc.Read');

    const metadata = await this.models.doc.getAuthors(workspace.id, pageId);
    if (!metadata) {
      throw new DocNotFound({ spaceId: workspace.id, docId: pageId });
    }

    return {
      createdAt: metadata.createdAt,
      updatedAt: metadata.updatedAt,
      createdBy: metadata.createdByUser || null,
      updatedBy: metadata.updatedByUser || null,
    };
  }

  @ResolveField(() => [DocType], {
    description: 'Get public docs of a workspace',
    complexity: 2,
  })
  async publicDocs(@Parent() workspace: WorkspaceType) {

View on GitHub (pinned to 2af30773ae)