toeverything/AFFiNE · error · ActionForbidden
action_forbidden
action_forbidden
Error message
This feature is temporarily unavailable for you.
What it means
Thrown by DocResolver.assertCanShare (used by publishDoc) when the runtime flags the acting user as invite-abuse quarantined or banned. The generic message is deliberately vague to avoid revealing anti-abuse state; the server logs the userId/workspaceId/action at warn level. ActionForbidden is an action_forbidden category error.
Source
Thrown at packages/backend/server/src/core/workspaces/resolvers/doc.ts:320
private readonly ac: PermissionAccess,
private readonly permission: PermissionService,
private readonly models: Models,
private readonly cache: Cache,
private readonly event: EventBus,
private readonly config: Config,
private readonly runtime: BackendRuntimeProvider
) {}
private async assertCanShare(
userId: string,
context: { workspaceId: string; docId: string; action: 'publishDoc' }
) {
if (await this.runtime.isInviteAbuseUserQuarantinedOrBanned(userId)) {
this.logger.warn('Share action blocked for quarantined actor', {
userId,
...context,
});
throw new ActionForbidden(
'This feature is temporarily unavailable for you.'
);
}
if (
await this.runtime.isInviteAbuseWorkspaceQuarantined(context.workspaceId)
) {
this.logger.warn('Share action blocked for quarantined workspace', {
userId,
...context,
});
throw new ActionForbidden(
'This feature is temporarily unavailable for you.'
);
}
const user = await this.models.user.get(userId);
const newAccountAgeMs = this.config.auth.newAccountShareActionDelay * 1000;
if (!user || !canUserExecuteLimitedActions(user, newAccountAgeMs)) {
this.logger.warn('Share action blocked for new account', {View on GitHub (pinned to 26c515e050)
Solutions
- Contact support/admin to review the abuse flag and lift the quarantine if it is a false positive.
- Wait out the quarantine window if it is time-boxed, then retry.
- Verify the user is not actually sending bulk invites that trigger the detection.
- Operators can adjust the abuse detection thresholds or review the user's abuse state in the runtime provider.
Defensive patterns
Strategy: try-catch
Validate before calling
// No reliable client-side check; abuse state is server-private. // Best practice: gate share UI behind a lightweight canShare probe query // that returns the server's verdict without mutating state.
Type guard
function isActionForbidden(e: unknown): boolean {
return (
typeof e === 'object' &&
e !== null &&
(e as any).extensions?.code === 'action_forbidden'
);
} Try / catch
try {
await gql.publishDoc({ workspaceId, docId, mode });
} catch (e) {
if (isActionForbidden(e)) {
notifyUser('Sharing is temporarily unavailable for your account. Contact support.');
return;
}
throw e;
} Prevention
- Do not blind-retry share actions on action_forbidden - it reflects a deliberate block.
- Surface a clear support path to the user rather than a generic error.
- Avoid invite patterns that may trip abuse detection in the first place.
When it happens
Trigger: Calling publishDoc (or any share action routed through assertCanShare) while runtime.isInviteAbuseUserQuarantinedOrBanned(userId) returns true - the user has been flagged by the invite-abuse detection system.
Common situations: A user sent many invites that were flagged as abuse; account caught in automated abuse quarantine; shared IP/ASN with abusive actors; false positive from aggressive abuse thresholds.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/c9f1517e7f167e24.
Report an issue: GitHub.