affaan-m/ECC · error

must use key=value form

Error message

{label} must use key=value form: {value}

What it means

parse_key_value_pairs expects each CLI value in KEY=VALUE form (e.g. VAR=x). When a supplied value lacks an '=' separator, it fails with '{label} must use key=value form: {value}'. A related bail also rejects empty keys or values.

Solutions

  1. Quote the whole pair in your shell: --env 'KEY=value'.
  2. Ensure there is exactly one '=' separating a non-empty key and a non-empty value.
  3. If the value itself contains '=', that's fine (split_once takes the first '='), but the key portion before it must be non-empty.
  4. Check with `echo` or shell tracing that the argument reached the program intact.

Example fix

// before
myapp run --env PATH --env DEBUG=1
// after
myapp run --env "PATH=/usr/bin" --env "DEBUG=1"
Defensive patterns

Strategy: validation

Validate before calling

fn is_key_value(s: &str) -> bool {
    match s.split_once('=') {
        Some((k, v)) => !k.trim().is_empty() && !v.trim().is_empty(),
        None => false,
    }
}
// validate each --env arg before invoking the command

Prevention

When it happens

Trigger: Passing flags like --env PATH or --env '=x' / 'KEY=' to options built on parse_key_value_pairs (env vars, headers, labels).

Common situations: Quoting mistakes in the shell that drop the '=', forgetting the value entirely, or using a shell that splits on spaces so only the key survives.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at ecc2/src/main.rs:8592

        to_id,
        &comms::MessageType::TaskHandoff {
            task: from_session.task,
            context,
            priority: comms::TaskPriority::Normal,
        },
    )
}

fn parse_template_vars(values: &[String]) -> Result<BTreeMap<String, String>> {
    parse_key_value_pairs(values, "template vars")
}

fn parse_key_value_pairs(values: &[String], label: &str) -> Result<BTreeMap<String, String>> {
    let mut vars = BTreeMap::new();
    for value in values {
        let (key, raw_value) = value
            .split_once('=')
            .ok_or_else(|| anyhow::anyhow!("{label} must use key=value form: {value}"))?;
        let key = key.trim();
        let raw_value = raw_value.trim();
        if key.is_empty() || raw_value.is_empty() {
            anyhow::bail!("{label} must use non-empty key=value form: {value}");
        }
        vars.insert(key.to_string(), raw_value.to_string());
    }
    Ok(vars)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::Config;
    use crate::session::store::StateStore;
    use crate::session::{Session, SessionMetrics, SessionState};
    use chrono::{Duration, Utc};
    use std::fs;

View on GitHub (pinned to 8321021c54)