paperclipai/paperclip · error · Error

actorUserId "${userId}" has viewer (read-only) access and ca

Error message

actorUserId "${userId}" has viewer (read-only) access and cannot take this write action

What it means

The actorUserId is an active member but has viewer (read-only) role, and the operation was called without { allowViewer: true }. The permission guard blocks viewer principals from write actions on behalf of board users.

Source

Thrown at server/src/services/plugin-host-services.ts:908

    companyId: string,
    userId: string,
    { allowViewer = false }: { allowViewer?: boolean } = {},
  ): Promise<void> => {
    const [membership] = await db
      .select({ id: companyMemberships.id, membershipRole: companyMemberships.membershipRole })
      .from(companyMemberships)
      .where(and(
        eq(companyMemberships.companyId, companyId),
        eq(companyMemberships.principalType, "user"),
        eq(companyMemberships.principalId, userId),
        eq(companyMemberships.status, "active"),
      ))
      .limit(1);
    if (!membership) {
      throw new Error(`actorUserId "${userId}" is not an active human member of this company`);
    }
    if (!allowViewer && membership.membershipRole === "viewer") {
      throw new Error(`actorUserId "${userId}" has viewer (read-only) access and cannot take this write action`);
    }
  };

  /**
   * Wake the assignee of a continuation issue after a plugin-relayed board-user
   * interaction resolution, mirroring the web app's board interaction routes
   * (routes/issues.ts's queueResolvedInteractionContinuationWakeup) so a
   * confirmation accepted/rejected from chat resumes the agent the same way it
   * would from the web app. Deliberately narrower than the HTTP helper: it
   * carries the core interaction context plus the plan-review continuation
   * payload (the confirmation kind the gateway resolves), and skips the
   * checkbox/tool-action/item-verdict extras that the gateway's yes/no decision
   * cards never produce. Failure-tolerant: a wake failure is logged, never
   * thrown back to the plugin (the decision itself already applied).
   */
  const queuePluginInteractionContinuationWakeup = (args: {
    issue: { id: string; assigneeAgentId: string | null; status: string };
    interaction: {

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Use an actorUserId with an editor/admin role for write actions; viewer-role users can only perform read operations. Ask a company admin to elevate the user's role if writes are required.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/plugin-host-services.ts:899 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18). Data as JSON: /api/errors/348e55c1786cb580. Report an issue: GitHub.