ruvnet/ruflo · error · Error

delegation-depth-exhausted

Error message

delegation-depth-exhausted

What it means

delegateEnvelope() implements bounded delegation: a parent envelope may spawn children only while parent.delegationDepth (defaulting to 0) is > 0. Each delegation decrements the depth, so attempting to delegate from an envelope at depth 0 — or one that never set the field — throws Error('delegation-depth-exhausted').

Source

Thrown at v3/@claude-flow/security/src/policy/envelope.ts:93

    [child.delegationDepth, parent.delegationDepth],
    [child.expiresAt, parent.expiresAt],
  ];
  return listChecks.every(Boolean)
    && numericChecks.every(([next, current]) => (
      current === undefined
        ? true
        : next !== undefined && next <= current
    ))
    && !(child.network === true && parent.network !== true)
    && !(child.destructive === true && parent.destructive !== true);
}

export function delegateEnvelope(
  parent: CapabilityEnvelope,
  child: CapabilityEnvelope,
): CapabilityEnvelope {
  if ((parent.delegationDepth ?? 0) <= 0) {
    throw new Error('delegation-depth-exhausted');
  }
  const reduced = {
    ...parent,
    ...child,
    delegationDepth: Math.min(
      child.delegationDepth ?? Number.MAX_SAFE_INTEGER,
      (parent.delegationDepth ?? 0) - 1,
    ),
    expiresAt: Math.min(
      child.expiresAt ?? Number.MAX_SAFE_INTEGER,
      parent.expiresAt ?? Number.MAX_SAFE_INTEGER,
    ),
  };
  if (!isEnvelopeReduction(parent, reduced)) throw new Error('capability-envelope-cannot-grow');
  return reduced;
}

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Set delegationDepth on the root envelope to the maximum chain length you need (e.g. 3 for root -> agent -> sub-agent -> sub-sub-agent).
  2. Check (parent.delegationDepth ?? 0) > 0 before attempting to delegate, and treat 0 as 'leaf only'.
  3. When a hierarchy deepens, mint a fresh root envelope with a higher depth rather than delegating past the cap.

Example fix

// before
const root = mintEnvelope({ principal: 'orchestrator', tools: [...] }); // delegationDepth undefined -> 0
const child = delegateEnvelope(root, { principal: 'worker', ... }); // throws

// after
const root = mintEnvelope({ principal: 'orchestrator', delegationDepth: 3, tools: [...] });
const child = delegateEnvelope(root, { principal: 'worker', ... });
Defensive patterns

Strategy: validation

Validate before calling

if ((parent.delegationDepth ?? 0) <= 0) {
  throw new Error(
    `envelope ${parent.id} is a leaf (delegationDepth 0) — cannot delegate`
  );
}
const child = delegateEnvelope(parent, childSpec);

Type guard

function canDelegate(parent: CapabilityEnvelope): boolean {
  return (parent.delegationDepth ?? 0) > 0;
}

Try / catch

try {
  return delegateEnvelope(parent, child);
} catch (err) {
  if (err instanceof Error && err.message === 'delegation-depth-exhausted') {
    return requestFreshRootEnvelope(parent.id); // mint from the authority instead
  }
  throw err;
}

Prevention

When it happens

Trigger: Creating a root envelope without delegationDepth and immediately calling delegateEnvelope(parent, child); an agent chain longer than the root's initial depth (grandchild of a depth-1 envelope); re-delegating an already-exhausted envelope in retry logic.

Common situations: Templates for capability envelopes that omit delegationDepth; adding a second tier of sub-agents to a hierarchy whose envelopes were minted with depth for only one tier; defensive envelopes intentionally issued with depth 0 being reused for delegation.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/1b8e1e0c26652497. Report an issue: GitHub.