affaan-m/ECC · error · anyhow::Error

Conflict messages require at least one --file

Error message

Conflict messages require at least one --file

What it means

Thrown when composing a `comms::MessageType::Conflict` message without any `--file` values. Conflict messages semantically describe a conflict on a specific file, so `files.first()` is required; the `ok_or_else` turns an empty list into this error. Other message kinds don't have this requirement, only Conflict.

Solutions

  1. Add at least one `--file <path>` argument to the message command.
  2. Ensure the shell variable feeding `--file` is non-empty (quote/guard it in scripts).
  3. If the message is not actually about a conflicting file, use a different message kind that has no file requirement.

Example fix

// before
$ ecc comms send --kind conflict --context "both edited main.rs"
error: Conflict messages require at least one --file
// after
$ ecc comms send --kind conflict --file src/main.rs --context "both edited main.rs"
Defensive patterns

Strategy: validation

Validate before calling

# Ensure at least one file is provided for conflict messages
if [ "$KIND" = "conflict" ] && [ -z "$FILE" ]; then
  echo "--kind conflict requires --file <path>" >&2; exit 2
fi

Prevention

When it happens

Trigger: Sending an agent-to-agent comm message with `--kind conflict` (MessageKindArg::Conflict) but omitting `--file <path>`; passing an empty files list via a scripted invocation.

Common situations: Users assume the free-text `--context`/message body suffices; shell variable holding filenames expands to empty; copying examples for another message kind (e.g. note) that don't need `--file`.

Understand the failure class

Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/4eb6942f81728445. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/main.rs:3936

    files: Vec<String>,
) -> Result<comms::MessageType> {
    Ok(match kind {
        MessageKindArg::Handoff => comms::MessageType::TaskHandoff {
            task: text,
            context: context.unwrap_or_default(),
            priority: priority.into(),
        },
        MessageKindArg::Query => comms::MessageType::Query { question: text },
        MessageKindArg::Response => comms::MessageType::Response { answer: text },
        MessageKindArg::Completed => comms::MessageType::Completed {
            summary: text,
            files_changed: files,
        },
        MessageKindArg::Conflict => {
            let file = files
                .first()
                .cloned()
                .ok_or_else(|| anyhow::anyhow!("Conflict messages require at least one --file"))?;
            comms::MessageType::Conflict {
                file,
                description: context.unwrap_or(text),
            }
        }
    })
}

fn format_remote_dispatch_action(action: &session::manager::RemoteDispatchAction) -> String {
    match action {
        session::manager::RemoteDispatchAction::SpawnedTopLevel => "spawned top-level".to_string(),
        session::manager::RemoteDispatchAction::Assigned(action) => match action {
            session::manager::AssignmentAction::Spawned => "spawned delegate".to_string(),
            session::manager::AssignmentAction::ReusedIdle => "reused idle delegate".to_string(),
            session::manager::AssignmentAction::ReusedActive => {
                "reused active delegate".to_string()
            }
            session::manager::AssignmentAction::DeferredSaturated => {

View on GitHub (pinned to 8321021c54)