{"record":{"id":"7290a2df7eb8e7b1","repo":"ruvnet/ruflo","slug":"cannot-delegate-revoked-capability-capability-id","errorCode":null,"errorMessage":"Cannot delegate revoked capability ${capability.id}","messagePattern":"Cannot delegate revoked capability (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/guidance/src/capabilities.ts","lineNumber":235,"sourceCode":"   * Creates a child capability with the new grantedTo agent. The parent\n   * capability must have delegatable=true. Optional further restrictions\n   * can be applied during delegation.\n   *\n   * @throws Error if the capability is not delegatable\n   */\n  delegate(\n    capability: Capability,\n    toAgentId: string,\n    restrictions?: Partial<Capability>,\n  ): Capability {\n    if (!capability.delegatable) {\n      throw new Error(\n        `Capability ${capability.id} is not delegatable`\n      );\n    }\n\n    if (capability.revoked) {\n      throw new Error(\n        `Cannot delegate revoked capability ${capability.id}`\n      );\n    }\n\n    if (capability.expiresAt !== null && capability.expiresAt <= Date.now()) {\n      throw new Error(\n        `Cannot delegate expired capability ${capability.id}`\n      );\n    }\n\n    const delegated: Capability = {\n      ...capability,\n      id: randomUUID(),\n      grantedBy: capability.grantedTo,\n      grantedTo: toAgentId,\n      grantedAt: Date.now(),\n      attestations: [],\n      parentCapabilityId: capability.id,","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/guidance/src/capabilities.ts#L217-L253","documentation":"delegate() refuses to derive children from a capability whose `revoked` flag is set, preventing revocation from being laundered by re-delegation. The check runs after the delegatable check, so this error confirms the parent was delegatable at grant time but has since been revoked. Note the error reflects the capability object's current state — a stale copy captured before revocation will not show it.","triggerScenarios":"Some component calls revoke() on the capability, then another calls delegate() with the same (or a stale copy of the) capability; revocation and delegation racing in concurrent workflows.","commonSituations":"Offboarding/revocation workflows racing against in-flight delegation jobs; cached capability objects captured before a revocation event; audit tooling re-running delegation scripts against revoked creds.","solutions":["Re-fetch the capability's current state from the authority before delegating and abort if revoked","If revocation was accidental, re-grant a fresh capability and delegate from that","Sequence revocation and delegation through one coordinator so they cannot interleave"],"exampleFix":"// before\nconst child = authority.delegate(cachedCap, 'agent-b'); // cachedCap was revoked meanwhile → throws\n\n// after\nconst fresh = authority.get(cachedCap.id);\nif (!fresh || fresh.revoked) throw new Error('capability no longer valid');\nconst child = authority.delegate(fresh, 'agent-b');","handlingStrategy":"validation","validationCode":"const fresh = authority.get(capability.id);\nif (!fresh || fresh.revoked) {\n  // capability is gone — abort delegation\n}","typeGuard":"const isActive = (c: Capability): boolean =>\n  !c.revoked && (c.expiresAt === null || c.expiresAt > Date.now());","tryCatchPattern":null,"preventionTips":["Never delegate from a cached capability object; re-fetch current state","Serialize revocation and delegation through one path so they cannot race","Propagate revocation events to anything holding capability copies"],"tags":["capabilities","authorization","revocation","guidance","race-condition"],"backgroundTag":"permission-revoked","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}