{"record":{"id":"c9f1517e7f167e24","repo":"toeverything/AFFiNE","slug":"action-forbidden-c9f151","errorCode":"action_forbidden","errorMessage":"This feature is temporarily unavailable for you.","messagePattern":"This feature is temporarily unavailable for you\\.","errorType":"exception","errorClass":"ActionForbidden","httpStatus":403,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/resolvers/doc.ts","lineNumber":320,"sourceCode":"    private readonly ac: PermissionAccess,\n    private readonly permission: PermissionService,\n    private readonly models: Models,\n    private readonly cache: Cache,\n    private readonly event: EventBus,\n    private readonly config: Config,\n    private readonly runtime: BackendRuntimeProvider\n  ) {}\n\n  private async assertCanShare(\n    userId: string,\n    context: { workspaceId: string; docId: string; action: 'publishDoc' }\n  ) {\n    if (await this.runtime.isInviteAbuseUserQuarantinedOrBanned(userId)) {\n      this.logger.warn('Share action blocked for quarantined actor', {\n        userId,\n        ...context,\n      });\n      throw new ActionForbidden(\n        'This feature is temporarily unavailable for you.'\n      );\n    }\n    if (\n      await this.runtime.isInviteAbuseWorkspaceQuarantined(context.workspaceId)\n    ) {\n      this.logger.warn('Share action blocked for quarantined workspace', {\n        userId,\n        ...context,\n      });\n      throw new ActionForbidden(\n        'This feature is temporarily unavailable for you.'\n      );\n    }\n    const user = await this.models.user.get(userId);\n    const newAccountAgeMs = this.config.auth.newAccountShareActionDelay * 1000;\n    if (!user || !canUserExecuteLimitedActions(user, newAccountAgeMs)) {\n      this.logger.warn('Share action blocked for new account', {","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/resolvers/doc.ts#L302-L338","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No reliable client-side check; abuse state is server-private.\n// Best practice: gate share UI behind a lightweight canShare probe query\n// that returns the server's verdict without mutating state.","typeGuard":"function isActionForbidden(e: unknown): boolean {\n  return (\n    typeof e === 'object' &&\n    e !== null &&\n    (e as any).extensions?.code === 'action_forbidden'\n  );\n}","tryCatchPattern":"try {\n  await gql.publishDoc({ workspaceId, docId, mode });\n} catch (e) {\n  if (isActionForbidden(e)) {\n    notifyUser('Sharing is temporarily unavailable for your account. Contact support.');\n    return;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["abuse-prevention","graphql","authorization","share"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}