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

must use non-empty key=value form

Error message

{label} must use non-empty key=value form: {value}

What it means

This CLI argument parser splits each `--var`/env-style value on `=` and rejects entries whose key or value is empty, e.g. `=foo`, `bar=`, `=`, or `key=`. It is a validation guard ensuring every variable inserted into the map has a meaningful key and value.

Solutions

  1. Ensure every variable is supplied as `KEY=value` with both parts non-empty.
  2. Check upstream environment variables are set before composing the CLI args.
  3. Quote the argument in your shell so `=` splitting behaves as intended and empty values fail loudly here rather than silently.
  4. Remove entries with empty values entirely if the variable is intentionally unset.

Example fix

// before
myapp run --env DEBUG=
// after
myapp run --env DEBUG=1
Defensive patterns

Strategy: validation

Validate before calling

fn validate_var(value: &str) -> Result<(), String> {
    match value.split_once('=') {
        Some((k, v)) if !k.trim().is_empty() && !v.trim().is_empty() => Ok(()),
        _ => Err(format!("bad key=value: {value}")),
    }
}

Prevention

When it happens

Trigger: Passing a flag value like `--env PATH=` or `--set =value`; whitespace-only key or value such as `--env ' = '` which trims to empty.

Common situations: Shell scripts building flags from environment variables where one variable is empty (`FOO=""` produces `FOO=`); copy-pasting examples with placeholder values left blank; template expansion dropping a value.

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/c84ab61b280036c9. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/main.rs:8596

            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;
    use std::path::{Path, PathBuf};

    struct TestDir {
        path: PathBuf,

View on GitHub (pinned to 8321021c54)