affaan-m/ECC · error

agent profile inheritance cycle: {}

Error message

agent profile inheritance cycle: {}

What it means

Runtime error from Config::resolve_agent_profile_inner in ecc2/src/config/mod.rs. The method walks the `inherits` chain and keeps a `chain` vec of visited names. If a name already appears in the chain, it is pushed again and the method bails with "agent profile inheritance cycle: {}" joined by ' -> '. The full path (e.g. "a -> b -> a") is included so the cycle is obvious.

Source

Thrown at ecc2/src/config/mod.rs:473

        }

        Ok(ResolvedOrchestrationTemplate {
            template_name: name.to_string(),
            description,
            project,
            task_group,
            steps,
        })
    }

    fn resolve_agent_profile_inner(
        &self,
        name: &str,
        chain: &mut Vec<String>,
    ) -> Result<ResolvedAgentProfile> {
        if chain.iter().any(|existing| existing == name) {
            chain.push(name.to_string());
            anyhow::bail!("agent profile inheritance cycle: {}", chain.join(" -> "));
        }

        let profile = self
            .agent_profiles
            .get(name)
            .ok_or_else(|| anyhow::anyhow!("Unknown agent profile: {name}"))?;

        chain.push(name.to_string());
        let mut resolved = if let Some(parent) = profile.inherits.as_deref() {
            self.resolve_agent_profile_inner(parent, chain)?
        } else {
            ResolvedAgentProfile::default()
        };
        chain.pop();

        resolved.apply(name, profile);
        Ok(resolved)
    }

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Read the cycle path printed in the message and break the back-edge in the named profile.
  2. Audit every `inherits =` in the agent_profiles section and ensure the graph is acyclic.
  3. Move shared defaults into a base profile that both children inherit from, rather than chaining siblings.
  4. Re-resolve after the edit to confirm the cycle is gone.

Example fix

# before
[agent_profiles.base]
inherits = "child"

[agent_profiles.child]
inherits = "base"

# after
[agent_profiles.base]

[agent_profiles.child]
inherits = "base"
Defensive patterns

Strategy: validation

Validate before calling

// Static check over the agent_profiles inherits graph before runtime.
fn inherits_graph_is_acyclic(cfg: &Config) -> Result<()> {
    let mut color: HashMap<&str, u8> = HashMap::new(); // 0=white,1=gray,2=black
    fn visit<'a>(cfg: &'a Config, n: &'a str, color: &mut HashMap<&'a str, u8>) -> Result<()> {
        match color.get(n).copied() {
            Some(2) => return Ok(()),
            Some(1) => anyhow::bail!("cycle detected at {n}"),
            _ => {}
        }
        color.insert(n, 1);
        if let Some(p) = cfg.agent_profile(n).and_then(|p| p.inherits.as_deref()) {
            visit(cfg, p, color)?;
        }
        color.insert(n, 2);
        Ok(())
    }
    for name in cfg.agent_profile_names() { visit(cfg, name, &mut color)?; }
    Ok(())
}

Type guard

fn profile_inherits_is_acyclic(cfg: &Config, start: &str) -> bool {
    let mut seen = HashSet::new();
    let mut cur = Some(start);
    while let Some(n) = cur {
        if !seen.insert(n.to_string()) {
            return false;
        }
        cur = cfg.agent_profile(n).and_then(|p| p.inherits.clone()).as_deref();
    }
    true
}

Try / catch

let profile = cfg.resolve_agent_profile(name).map_err(|e| {
    if e.to_string().contains("inheritance cycle") {
        anyhow::anyhow!("{e}; remove the back-edge in the inherits chain")
    } else {
        e
    }
})?;

Prevention

When it happens

Trigger: Defining agent profiles where the `inherits` graph has a cycle: profile A inherits B and B inherits A, or any longer loop. Calling resolve_agent_profile on any node in that cycle triggers the guard.

Common situations: Refactoring profiles introduced a back-edge; copy-paste of an `inherits` line created a mutual dependency; a profile was redirected to inherit from a child during reorganization; chained inheritance that worked became cyclic after a rename.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/3890510ffdb12957. Report an issue: GitHub.