affaan-m/ECC · error
orchestration template
Error message
orchestration template {name} has no steps What it means
This error is thrown when resolving an orchestration template by name: the template exists in the configuration, but its `steps` list is empty. The library refuses to instantiate a template that would produce zero steps, since an orchestration with no steps is meaningless and would silently do nothing. It is a defensive configuration sanity check performed after template lookup succeeds.
Solutions
- Add at least one step to the `steps` array of the named orchestration template in your config file.
- Check the template name in your invocation — you may be referencing a stub template instead of the fully populated one.
- Validate the config before use: iterate all orchestration_templates and fail fast on any with empty steps.
- If the template is intentionally empty, remove it from config rather than leaving an empty definition.
Example fix
# before (config)
[orchestration_templates.deploy]
description = "Deploy pipeline"
steps = []
# after
[orchestration_templates.deploy]
description = "Deploy pipeline"
steps = [
{ name = "build", agent = "builder" },
{ name = "verify", agent = "code-reviewer" }
] Defensive patterns
Strategy: validation
Validate before calling
for (name, tpl) in &config.orchestration_templates {
if tpl.steps.is_empty() {
return Err(anyhow!("template '{name}' has no steps"));
}
} Type guard
fn has_steps(tpl: &OrchestrationTemplate) -> bool { !tpl.steps.is_empty() } Try / catch
match resolver.resolve_template(name, &vars) {
Ok(t) => t,
Err(e) if e.to_string().contains("has no steps") => {
eprintln!("Template '{name}' is empty; check config");
Default::default()
}
Err(e) => return Err(e),
} Prevention
- Never commit templates with empty steps arrays; lint the config file in CI.
- Give every new template a placeholder step immediately after creating it.
- Keep template definitions in one reviewed config file rather than scattering edits.
- Unit-test resolution of every declared template at startup.
When it happens
Trigger: Calling the template-resolution code (e.g. resolving/rendering an orchestration template by name) where the named template is present in `orchestration_templates` but its `steps` field is an empty array or was never populated.
Common situations: A TOML/JSON config file declares an orchestration template section with description/project/agent fields but omits the steps list; a migration or hand-edit stripped the steps; a template was copy-pasted as a stub and never filled in; a programmatic config builder created the template struct without pushing steps.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- candidate configuration exceeds 1 MiB
- missing orchestration template variable(s)
- worktree_branch_prefix cannot be empty
- agent profile inheritance cycle
- application config exceeds local size limit
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/02c0a43823eb4833.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/config/mod.rs:403
}
pub fn harness_runner(&self, harness: &str) -> Option<&HarnessRunnerConfig> {
let key = harness.trim().to_ascii_lowercase();
self.harness_runners.get(&key)
}
pub fn resolve_orchestration_template(
&self,
name: &str,
vars: &BTreeMap<String, String>,
) -> Result<ResolvedOrchestrationTemplate> {
let template = self
.orchestration_templates
.get(name)
.ok_or_else(|| anyhow::anyhow!("Unknown orchestration template: {name}"))?;
if template.steps.is_empty() {
anyhow::bail!("orchestration template {name} has no steps");
}
let description = interpolate_optional_string(template.description.as_deref(), vars)?;
let project = interpolate_optional_string(template.project.as_deref(), vars)?;
let task_group = interpolate_optional_string(template.task_group.as_deref(), vars)?;
let default_agent = interpolate_optional_string(template.agent.as_deref(), vars)?;
let default_profile = interpolate_optional_string(template.profile.as_deref(), vars)?;
if let Some(profile_name) = default_profile.as_deref() {
self.resolve_agent_profile(profile_name)?;
}
let mut steps = Vec::with_capacity(template.steps.len());
for (index, step) in template.steps.iter().enumerate() {
let task = interpolate_required_string(&step.task, vars).with_context(|| {
format!(
"resolve task for orchestration template {name} step {}",
index + 1
)View on GitHub (pinned to 8321021c54)