paperclipai/paperclip · error

actorUserId is required to decide an approval on behalf of a

Error message

actorUserId is required to decide an approval on behalf of a board user

What it means

approvals.decide() was called without actorUserId. Approval decisions are board-user actions; the host requires the acting human user to re-verify their membership and record attribution.

Source

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

      async get(params) {
        const companyId = ensureCompanyId(params.companyId);
        await ensurePluginAvailableForCompany(companyId);
        const approval = await approvalSvc.getById(params.approvalId);
        if (!approval || approval.companyId !== companyId) return null;
        return redactApprovalPayload(approval) as any;
      },
      async decide(params) {
        const companyId = ensureCompanyId(params.companyId);
        await ensurePluginAvailableForCompany(companyId);
        const existing = await approvalSvc.getById(params.approvalId);
        if (!existing || existing.companyId !== companyId) {
          throw new Error(`Approval "${params.approvalId}" not found`);
        }
        // Deciding an approval is a board-user action (the web app's approval
        // decision routes are board-only). Re-verify active membership at apply
        // time; never trust the plugin-supplied identity.
        if (!params.actorUserId) {
          throw new Error("actorUserId is required to decide an approval on behalf of a board user");
        }
        await requireActiveHumanMember(companyId, params.actorUserId);

        const { approval, applied } = params.action === "approve"
          ? await approvalSvc.approve(params.approvalId, params.actorUserId, params.decisionNote ?? null)
          : await approvalSvc.reject(params.approvalId, params.actorUserId, params.decisionNote ?? null);

        await logPluginActivity({
          companyId,
          action: params.action === "approve" ? "approval.approved" : "approval.rejected",
          entityType: "approval",
          entityId: approval.id,
          actor: { actorUserId: params.actorUserId },
          details: {
            type: approval.type,
            requestedByAgentId: approval.requestedByAgentId,
            applied,
          },

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Provide actorUserId when deciding an approval on behalf of a board user; approvals cannot be decided anonymously by a plugin.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/plugin-host-services.ts:2659 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/b7671fff0bed2863. Report an issue: GitHub.