{"record":{"id":"ecab66e5a1e8d3d2","repo":"nanocoai/nanoclaw","slug":"forbidden","errorCode":"forbidden","errorMessage":"Wiring not found for this conversation.","messagePattern":"Wiring not found for this conversation\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/dispatch.ts","lineNumber":96,"sourceCode":"\n    if (cliScope === 'group') {\n      // Auto-fill agent-group-related args so the agent doesn't need\n      // to pass its own group ID explicitly.\n      const fill: Record<string, unknown> = {\n        agent_group_id: req.args.agent_group_id ?? ctx.agentGroupId,\n        group: req.args.group ?? ctx.agentGroupId,\n      };\n      // Only auto-fill --id for resources where it IS the agent group ID\n      // (groups, destinations). For sessions/members --id is a different key.\n      if (cmd.resource === 'groups' || cmd.resource === 'destinations') {\n        fill.id = req.args.id ?? ctx.agentGroupId;\n      }\n\n      // Group-scoped agents may only inspect or update the wiring for the\n      // conversation they are currently serving. Never trust a caller ID.\n      if (req.args.help !== true && (req.command === 'wirings-get' || req.command === 'wirings-update')) {\n        const wiring = await getMessagingGroupAgentByPair(ctx.messagingGroupId, ctx.agentGroupId);\n        if (!wiring) return err(req.id, 'forbidden', 'Wiring not found for this conversation.');\n        fill.id = wiring.id;\n      }\n      req = { ...req, args: { ...req.args, ...fill } };\n\n      // Fail-closed pre-handler check for sessions-get/-history: returns\n      // \"not found\" regardless of whether the UUID exists in another group,\n      // preventing an existence oracle across group boundaries. (history\n      // also self-scopes in its handler — this is defense-in-depth.)\n      if (\n        cmd.resource === 'sessions' &&\n        (req.command === 'sessions-get' || req.command === 'sessions-history') &&\n        req.args.id\n      ) {\n        const s = await getSession(req.args.id as string);\n        if (!s || s.agent_group_id !== ctx.agentGroupId) {\n          return err(req.id, 'handler-error', `session not found: ${req.args.id}`);\n        }\n      }","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/cli/dispatch.ts#L78-L114","documentation":"A group-scoped agent tried to get/update the wiring for a conversation its agent group is not actually wired to. The dispatcher resolves the wiring by the (messagingGroupId, agentGroupId) pair from the caller context — never from a caller-supplied ID — and returns `forbidden` when no such wiring exists. This is deliberate scope enforcement so an agent can't inspect or edit wirings of other groups.","triggerScenarios":"An agent in `cli_scope: group` sends `wirings-get`/`wirings-update` while serving a session whose messaging group was unwired from this agent group, or after rewiring the chat to a different agent group.","commonSituations":"Operator rewired the channel to another group while the agent session was live; the session was created under an old wiring and is now orphaned; agent replaying an old command with a stale wiring id.","solutions":["Verify the wiring still exists: `ncl wirings list` and look for the (messaging group, agent group) pair","Re-wire the messaging group to this agent group if it was removed intentionally-typo'd away","Restart/let the session re-resolve so the agent context reflects the current wiring","For legitimate cross-group wiring inspection, an operator (not the agent) must run it host-side"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const wirings = await runNcl('wirings list --json');\nconst mine = wirings.find(w => w.messaging_group_id === ctx.messagingGroupId && w.agent_group_id === ctx.agentGroupId);\nif (!mine) throw new Error('not wired — ask the operator to re-wire');","typeGuard":"function wiringExistsForPair(list: Array<{messaging_group_id: string; agent_group_id: string}>, mgId: string, agId: string): boolean {\n  return list.some(w => w.messaging_group_id === mgId && w.agent_group_id === agId);\n}","tryCatchPattern":"Match code === 'forbidden' for wirings-get/update and fall back to informing the user the chat isn't wired to this agent.","preventionTips":["Treat wirings as operator-owned state; verify wiring exists before offering wirings commands to agents","Re-check wiring after any rewire or migration","Don't cache wiring ids across sessions"],"tags":["cli","ncl","wirings","forbidden","group-scope"],"backgroundTag":"authorization-scope-denied","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}