{"record":{"id":"b7671fff0bed2863","repo":"paperclipai/paperclip","slug":"actoruserid-is-required-to-decide-an-approval-on-b","errorCode":null,"errorMessage":"actorUserId is required to decide an approval on behalf of a board user","messagePattern":"actorUserId is required to decide an approval on behalf of a board user","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/plugin-host-services.ts","lineNumber":2668,"sourceCode":"      async get(params) {\n        const companyId = ensureCompanyId(params.companyId);\n        await ensurePluginAvailableForCompany(companyId);\n        const approval = await approvalSvc.getById(params.approvalId);\n        if (!approval || approval.companyId !== companyId) return null;\n        return redactApprovalPayload(approval) as any;\n      },\n      async decide(params) {\n        const companyId = ensureCompanyId(params.companyId);\n        await ensurePluginAvailableForCompany(companyId);\n        const existing = await approvalSvc.getById(params.approvalId);\n        if (!existing || existing.companyId !== companyId) {\n          throw new Error(`Approval \"${params.approvalId}\" not found`);\n        }\n        // Deciding an approval is a board-user action (the web app's approval\n        // decision routes are board-only). Re-verify active membership at apply\n        // time; never trust the plugin-supplied identity.\n        if (!params.actorUserId) {\n          throw new Error(\"actorUserId is required to decide an approval on behalf of a board user\");\n        }\n        await requireActiveHumanMember(companyId, params.actorUserId);\n\n        const { approval, applied } = params.action === \"approve\"\n          ? await approvalSvc.approve(params.approvalId, params.actorUserId, params.decisionNote ?? null)\n          : await approvalSvc.reject(params.approvalId, params.actorUserId, params.decisionNote ?? null);\n\n        await logPluginActivity({\n          companyId,\n          action: params.action === \"approve\" ? \"approval.approved\" : \"approval.rejected\",\n          entityType: \"approval\",\n          entityId: approval.id,\n          actor: { actorUserId: params.actorUserId },\n          details: {\n            type: approval.type,\n            requestedByAgentId: approval.requestedByAgentId,\n            applied,\n          },","sourceCodeStart":2650,"sourceCodeEnd":2686,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/services/plugin-host-services.ts#L2650-L2686","documentation":"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.","triggerScenarios":"Thrown at server/src/services/plugin-host-services.ts:2659 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Provide actorUserId when deciding an approval on behalf of a board user; approvals cannot be decided anonymously by a plugin."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}