stamparm/maltrail · error · ConfigError

missing configuration file

Error message

missing configuration file '{}'

What it means

Config::load requires the given path to be an existing regular file. If it isn't, it bails with ConfigError 'missing configuration file ...' before attempting to read. This is the sensor's first gate when starting with a config path, distinguishing a missing file from an unreadable one (which produces a different message).

Solutions

  1. Verify the path exists: ls the exact path you pass and create the config if missing (copy sensor.conf example)
  2. Use an absolute path for --config to avoid CWD-dependent resolution
  3. Check your deployment/startup script actually wrote the config before launching the sensor
  4. Confirm the path points to a file, not a directory

Example fix

// before
let cfg = Config::load(Path::new("sensor.conf"))?;
// after
let path = PathBuf::from("sensor.conf");
assert!(path.is_file(), "missing config at {}", path.display());
let cfg = Config::load(&path)?;
Defensive patterns

Strategy: validation

Validate before calling

use std::path::{Path, PathBuf};
fn ensure_config(path: &Path) -> Option<PathBuf> {
    if path.is_file() { Some(path.to_path_buf()) } else { None }
}

Try / catch

match Config::load(&config_file) {
    Ok(cfg) => cfg,
    Err(ConfigError(msg)) if msg.starts_with("missing configuration file") => {
        eprintln!("[!] {msg}; pass -c with a valid path");
        std::process::exit(2);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Starting the sensor (or calling Config::load) with a path that does not exist or is a directory — wrong -c/--config value, config file deleted, relative path resolved against an unexpected working directory, or a config generated at runtime that wasn't created yet.

Common situations: Passing a default config path that was never created; running the binary from a different CWD so a relative path breaks; a service/deploy step that should generate sensor.conf failed silently; typo in the config filename.

Understand the failure class

Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.

Related errors


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/cf1f0811032793d6. Report an issue: GitHub.

Appendix: source

Thrown at sensor/src/config.rs:734

/// semicolons or whitespace. `core/log.py:_endpoints()` splits the same way.
pub fn split_endpoints(value: &str) -> Vec<&str> {
    value.split([',', ';', ' ', '\t', '\n', '\r']).map(str::trim).filter(|s| !s.is_empty()).collect()
}

/// `sensor.py:_cfg_bool()` — for switches without a boolean-implying prefix.
pub fn cfg_bool(value: Option<&Value>) -> bool {
    match value {
        Some(Value::Bool(b)) => *b,
        Some(Value::Int(i)) => *i == 1,
        Some(Value::Str(s)) => matches!(s.trim().to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"),
        _ => false,
    }
}

impl Config {
    pub fn load(config_file: &Path) -> Result<Config, ConfigError> {
        if !config_file.is_file() {
            bail!("missing configuration file '{}'", config_file.display());
        }
        crate::cprintln!("[i] using configuration file '{}'", config_file.display());
        let content = std::fs::read_to_string(config_file)
            .map_err(|e| ConfigError(format!("unable to read configuration file '{}' ({e})", config_file.display())))?;

        let root = settings::resolve_root(config_file);
        let raw = parse_raw(&content, &root)?;

        for option in ["MONITOR_INTERFACE", "CAPTURE_BUFFER", "LOG_DIR"] {
            if !raw.contains_key(option) {
                bail!("missing mandatory option '{option}' in configuration file '{}'", config_file.display());
            }
        }

        for name in unknown_keys(&raw) {
            // A typo'd name parses fine and is then ignored - the feature it was meant to
            // configure just stays off while the file looks correct. Warn rather than fail: an
            // older config meeting a newer sensor (or the reverse) must keep working.

View on GitHub (pinned to 77cfb06d76)