stamparm/maltrail · error · ConfigError

invalid configuration value for 'LOCAL_LOG_FORMAT

Error message

invalid configuration value for 'LOCAL_LOG_FORMAT' ('{other}'), expected 'text' or 'json'

What it means

LOCAL_LOG_FORMAT selects the on-disk log format and must be empty, 'text'/'plain', or 'json'/'ndjson' (case-insensitive, whitespace-trimmed). Any other string bails with this message showing the offending value and the two accepted forms.

Solutions

  1. Set LOCAL_LOG_FORMAT=text (or 'plain') for human-readable logs, or json (or 'ndjson') for machine-readable logs.
  2. Leave the value empty to get the default (text) format.
  3. Check for typos and remove surrounding quotes or extra characters not trimmed by whitespace.
  4. Consult the sensor docs for the exact accepted tokens.

Example fix

// before (config.conf)
LOCAL_LOG_FORMAT=csv

// after (config.conf)
LOCAL_LOG_FORMAT=json
Defensive patterns

Strategy: validation

Validate before calling

let v = get_str(&raw, "LOCAL_LOG_FORMAT").trim().to_ascii_lowercase();
if !(v.is_empty() || ["text", "plain", "json", "ndjson"].contains(&v.as_str())) {
    eprintln!("LOCAL_LOG_FORMAT must be 'text' or 'json', got '{v}'");
}

Prevention

When it happens

Trigger: Setting LOCAL_LOG_FORMAT to an unsupported token such as 'Textfile', 'csv', 'structured', 'syslog', or a misspelling like 'jason'; also values with stray inner characters after trimming.

Common situations: Admins guessing format names from other products (csv, logfmt); capitalization assumptions are fine (handled), but synonyms like 'logfmt' are not; copy-paste from documentation of a different tool.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at sensor/src/config.rs:945

                    ConfigError(format!("invalid configuration value for 'CAPTURE_BUFFER_SIZE' ('{v}'): {e}"))
                })?
            } else if capture_buffer > 0 {
                capture_buffer.clamp(DEFAULT_CAPTURE_RING, MAX_INFERRED_CAPTURE_RING)
            } else {
                DEFAULT_CAPTURE_RING
            }
        };

        // LOCAL_LOG_FORMAT: an unknown value is refused rather than silently treated as "text",
        // because the whole point of setting it is that something downstream is expecting the
        // other format.
        let local_log_json = {
            let v = get_str(&raw, "LOCAL_LOG_FORMAT");
            match v.trim().to_ascii_lowercase().as_str() {
                "" | "text" | "plain" => false,
                "json" | "ndjson" => true,
                other => {
                    bail!("invalid configuration value for 'LOCAL_LOG_FORMAT' ('{other}'), expected 'text' or 'json'")
                }
            }
        };

        let capture_fanout_mode = {
            let v = get_str(&raw, "CAPTURE_FANOUT_MODE");
            if v.trim().is_empty() {
                // Unset. A single worker never forms a fanout group, so the mode is moot there.
                // Above one it decides whether the scan heuristics survive being split at all -
                // flow hashing costs 34% of them at 8 workers - so the default is the mode that
                // does not quietly trade detections for throughput. An operator who wants the old
                // behaviour asks for it by name.
                if capture_workers > 1 {
                    FanoutMode::Source
                } else {
                    FanoutMode::Hash
                }
            } else {

View on GitHub (pinned to 77cfb06d76)