stamparm/maltrail · error
config must load
Error message
config must load
What it means
Same test-helper expect: `Config::load(&path).expect("config must load")` asserts that a base config plus PROCESS_COUNT 16 loads cleanly. The test documents that PROCESS_COUNT must not influence capture_workers; the expect fails only when Config::load rejects the file outright.
Solutions
- Inspect the Err from Config::load for the exact failing key
- Adjust the new validation to accept the minimal base config
- Update the fixture if a genuinely required field is missing
Example fix
// before let base = "MONITOR_INTERFACE any\nCAPTURE_BUFFER 1MB\nLOG_DIR /tmp\n"; // after let base = "MONITOR_INTERFACE any\nCAPTURE_BUFFER 1MB\nLOG_DIR /tmp\nUPDATE_PERIOD 86400\n";
Defensive patterns
Strategy: try-catch
Validate before calling
let cfg = Config::load(&path);
assert!(cfg.is_ok(), "PROCESS_COUNT config rejected: {:?}", cfg.err()); Try / catch
match Config::load(&path) {
Ok(cfg) => proceed(cfg),
Err(e) => eprintln!("config load failed: {e}"),
} Prevention
- Add any new required field to test base fixtures immediately
- Run the full config test module when touching Config::load
When it happens
Trigger: Config::load returns Err for the base config (MONITOR_INTERFACE any / CAPTURE_BUFFER 1MB / LOG_DIR /tmp / UPDATE_PERIOD 86400) plus 'PROCESS_COUNT 16', e.g. after a new required-field or range check was introduced.
Common situations: Adding validation (e.g. rejecting unknown keys or making a new field mandatory) that breaks previously-loading minimal configs.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/dc11f77f808c34a5.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/config.rs:1343
assert!(split_endpoints("").is_empty());
// EVERY endpoint is validated: a typo in the second is as fatal as one in the first,
// because forwarding to one of two configured collectors is silent half-failure.
assert!(write("bad2.conf", "SYSLOG_SERVER 1.2.3.4:514, nonsense\n").is_err());
assert!(write("bad1.conf", "SYSLOG_SERVER nonsense, 1.2.3.4:514\n").is_err());
assert!(write("badls.conf", "LOGSTASH_SERVER 1.2.3.4:5000, 5.6.7.8\n").is_err());
}
#[test]
fn worker_count_is_opt_in() {
let dir = std::env::temp_dir().join("mt-cfg-workers");
let _ = std::fs::create_dir_all(&dir);
let base = "MONITOR_INTERFACE any\nCAPTURE_BUFFER 1MB\nLOG_DIR /tmp\nUPDATE_PERIOD 86400\n";
let write = |name: &str, extra: &str| {
let path = dir.join(name);
std::fs::write(&path, format!("{base}{extra}")).unwrap();
Config::load(&path).expect("config must load")
};
// PROCESS_COUNT alone must NOT fan out: it is sensor.py's worker-process count, and
// honouring it here degraded the scan heuristics of anyone who never touched the setting.
assert_eq!(write("pc.conf", "PROCESS_COUNT 16\n").capture_workers, 1);
// Both explicit knobs still work, and still win.
assert_eq!(write("cw.conf", "CAPTURE_WORKERS 4\n").capture_workers, 4);
assert_eq!(write("cf.conf", "CAPTURE_FANOUT 8\n").capture_workers, 8);
assert!(write("auto.conf", "CAPTURE_WORKERS auto\n").capture_workers >= 1);
}
#[test]
fn fanout_defaults_to_source_affinity_only_when_it_matters() {
let dir = std::env::temp_dir().join("mt-cfg-fanout-default");
let _ = std::fs::create_dir_all(&dir);
let base = "MONITOR_INTERFACE any\nCAPTURE_BUFFER 1MB\nLOG_DIR /tmp\nUPDATE_PERIOD 86400\n";
let write = |name: &str, extra: &str| {
let path = dir.join(name);View on GitHub (pinned to 77cfb06d76)