affaan-m/ECC · error · anyhow::Error
Configured harness runner for
Error message
Configured harness runner for {runner_key} is missing a program What it means
agent_program resolves the executable for a harness runner. If a custom harness runner is configured for runner_key but its program field is empty (or whitespace-only) after trimming, the library bails instead of silently falling back to the default binary. This catches malformed runner configuration that would otherwise produce a cryptic spawn failure.
Solutions
- Set the program field to the executable path (e.g., /usr/local/bin/claude) in the harness runner config
- Remove the runner config entry entirely so the library falls back to the built-in default (claude/codex/opencode/gemini)
- Validate the runner config at startup: trim and check non-empty program for every configured runner
- Ensure env-var interpolation used for program is not resolving to an empty string
Example fix
# before
cfg:
harness_runner:
claude-pro:
program: ""
# after
cfg:
harness_runner:
claude-pro:
program: "/usr/local/bin/claude" Defensive patterns
Strategy: validation
Validate before calling
fn validate_runner_config(cfg: &Config) -> Result<(), String> {
for (key, runner) in cfg.all_harness_runners() {
if runner.program.trim().is_empty() {
return Err(format!("harness runner '{key}' has empty program"));
}
}
Ok(())
} Type guard
fn has_program(r: &HarnessRunner) -> bool { !r.program.trim().is_empty() } Try / catch
match launch(...) {
Err(e) if e.to_string().contains("is missing a program") => {
eprintln!("fix harness_runner config: set a non-empty program or remove the entry");
Err(e)
}
other => other,
} Prevention
- Validate runner configs at startup before launching sessions
- Never write empty program values; omit the entry to use defaults
- Check env-var interpolation doesn't produce empty program strings
When it happens
Trigger: cfg.harness_runner(&runner_key) returns Some(runner) whose runner.program.trim() is empty; e.g., config with an entry like [harness_runner.mykey] program = "".
Common situations: A partially filled config where a user created a runner section but left program blank; template/config generation writing empty strings for program; env var interpolation yielding empty string; copying a config template and forgetting to set program.
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
- application config exceeds local size limit
- Asset request_id/modality does not match the bundle
- at least one genre spec is required
- bundle collections must be lists
- candidate configuration exceeds 1 MiB
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/c47897b8594acac6.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/session/manager.rs:2335
session.state
);
}
if let Some(worktree) = session.worktree.as_ref() {
let _ = crate::worktree::remove(worktree);
}
db.delete_session(&session.id)?;
Ok(())
}
fn agent_program(cfg: &Config, agent_type: &str) -> Result<PathBuf> {
let harness = HarnessKind::from_agent_type(agent_type);
let runner_key = SessionHarnessInfo::runner_key(agent_type);
if let Some(runner) = cfg.harness_runner(&runner_key) {
let program = runner.program.trim();
if program.is_empty() {
anyhow::bail!("Configured harness runner for {runner_key} is missing a program");
}
return Ok(PathBuf::from(program));
}
match harness {
HarnessKind::Claude => Ok(PathBuf::from("claude")),
HarnessKind::Codex => Ok(PathBuf::from("codex")),
HarnessKind::OpenCode => Ok(PathBuf::from("opencode")),
HarnessKind::Gemini => Ok(PathBuf::from("gemini")),
other => anyhow::bail!("Unsupported agent type: {other}"),
}
}
fn resolve_session(db: &StateStore, id: &str) -> Result<Session> {
let session = if id == "latest" {
db.get_latest_session()?
} else {
db.get_session(id)?View on GitHub (pinned to 8321021c54)