ruvnet/ruflo · error
Cannot delegate revoked capability ${capability.id}
Error message
Cannot delegate revoked capability ${capability.id} What it means
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.
Source
Thrown at v3/@claude-flow/guidance/src/capabilities.ts:235
* Creates a child capability with the new grantedTo agent. The parent
* capability must have delegatable=true. Optional further restrictions
* can be applied during delegation.
*
* @throws Error if the capability is not delegatable
*/
delegate(
capability: Capability,
toAgentId: string,
restrictions?: Partial<Capability>,
): Capability {
if (!capability.delegatable) {
throw new Error(
`Capability ${capability.id} is not delegatable`
);
}
if (capability.revoked) {
throw new Error(
`Cannot delegate revoked capability ${capability.id}`
);
}
if (capability.expiresAt !== null && capability.expiresAt <= Date.now()) {
throw new Error(
`Cannot delegate expired capability ${capability.id}`
);
}
const delegated: Capability = {
...capability,
id: randomUUID(),
grantedBy: capability.grantedTo,
grantedTo: toAgentId,
grantedAt: Date.now(),
attestations: [],
parentCapabilityId: capability.id,View on GitHub (pinned to fa13ee4ad6)
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
Example fix
// before
const child = authority.delegate(cachedCap, 'agent-b'); // cachedCap was revoked meanwhile → throws
// after
const fresh = authority.get(cachedCap.id);
if (!fresh || fresh.revoked) throw new Error('capability no longer valid');
const child = authority.delegate(fresh, 'agent-b'); Defensive patterns
Strategy: validation
Validate before calling
const fresh = authority.get(capability.id);
if (!fresh || fresh.revoked) {
// capability is gone — abort delegation
} Type guard
const isActive = (c: Capability): boolean => !c.revoked && (c.expiresAt === null || c.expiresAt > Date.now());
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Capability ${capability.id} is not delegatable
- Cannot delegate expired capability ${capability.id}
- Cannot compose capabilities with different scopes: "${cap1.s
- Cannot compose capabilities with different resources: "${cap
- Issue ${input.issueId} is not claimed by ${input.claimantId}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/7290a2df7eb8e7b1.
Report an issue: GitHub.