{"record":{"id":"9929ef59d7543dc9","repo":"paperclipai/paperclip","slug":"actoruserid-is-required-to-respond-to-an-interacti","errorCode":null,"errorMessage":"actorUserId is required to respond to an interaction on behalf of a board user","messagePattern":"actorUserId is required to respond to an interaction on behalf of a board user","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/plugin-host-services.ts","lineNumber":2505,"sourceCode":"        });\n        return interaction as any;\n      },\n      async listInteractions(params) {\n        const companyId = ensureCompanyId(params.companyId);\n        await ensurePluginAvailableForCompany(companyId);\n        if (!inCompany(await issues.getById(params.issueId), companyId)) return [];\n        return (await interactions.listForIssue(params.issueId)) as any;\n      },\n      async respondInteraction(params) {\n        const companyId = ensureCompanyId(params.companyId);\n        await ensurePluginAvailableForCompany(companyId);\n        const issue = requireInCompany(\"Issue\", await issues.getById(params.issueId), companyId);\n        // Resolving an interaction is a board-user action (the web app's\n        // interaction-resolve routes are board-only). The host re-verifies the\n        // paired user is an active human member at apply time and never trusts\n        // the plugin-supplied identity — matching the createComment bar.\n        if (!params.actorUserId) {\n          throw new Error(\"actorUserId is required to respond to an interaction on behalf of a board user\");\n        }\n        await requireActiveHumanMember(companyId, params.actorUserId);\n\n        const current = await interactions.getById(params.interactionId);\n        if (!current || current.issueId !== issue.id || current.companyId !== companyId) {\n          throw new Error(`Interaction \"${params.interactionId}\" not found for this issue`);\n        }\n        // Idempotent replay: an already-resolved interaction converges without\n        // re-applying, so a duplicate button tap from chat is a safe no-op.\n        if (current.status !== \"pending\") {\n          return { interaction: current as any, applied: false };\n        }\n\n        const actor = { userId: params.actorUserId };\n        let resolved: typeof current;\n        let continuationTarget = {\n          id: issue.id,\n          assigneeAgentId: issue.assigneeAgentId,","sourceCodeStart":2487,"sourceCodeEnd":2523,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/services/plugin-host-services.ts#L2487-L2523","documentation":"respondInteraction was invoked without an actorUserId. Resolving an issue-thread interaction is a board-user action, so the host requires a human actor to attribute the response to before re-verifying their membership.","triggerScenarios":"Thrown at server/src/services/plugin-host-services.ts:2496 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Include actorUserId in the respond-to-interaction call when acting on behalf of a board user; obtain it from the interaction request context."],"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"}