stamparm/maltrail · error
settings::init() must run before statics()
Error message
settings::init() must run before statics()
What it means
`settings::statics()` reads a lazily-initialized global (`STATICS`, typically a `OnceLock`) and `.expect()`s that `settings::init()` was called first. Calling `statics()` before initialization panics with 'settings::init() must run before statics()'. This is an initialization-order contract: compiled settings/static tables can only be built after user configuration is loaded.
Solutions
- Call `settings::init()` (with the required config) at the start of the entry point or test before anything touches `statics()`.
- In tests, add an init call or a shared `once` fixture (e.g. a `#[ctor]`/`OnceCell` helper) so every test that transitively uses `statics()` is covered.
- Make `statics()` resilient if appropriate: return `Option<&'static Statics>` or fall back to defaults, converting the panic into a clear init error.
- Audit all binaries/threads: ensure init happens before spawning anything that reads settings, and consider asserting initialization in a startup self-check.
Example fix
// before
let statics = settings::statics(); // panics if init not yet run
// after
settings::init(&config).expect("settings init failed");
let statics = settings::statics(); Defensive patterns
Strategy: type-guard
Validate before calling
// caller-side pre-check
fn statics_ready() -> bool { settings::initialized() } // or STATICS.get().is_some() Type guard
fn try_statics() -> Option<&'static Statics> { settings::try_statics() } Try / catch
// std::sync::OnceLock based
let statics = STATICS.get().unwrap_or_else(||
panic!("settings::init() must run before statics(); call it in main()/test setup")); Prevention
- Call settings::init() as the first statement of every entry point and test suite
- Add a shared test fixture (#[ctor] or OnceCell) that initializes settings
- Avoid reading statics from threads spawned before init
- Consider a startup self-check that fails fast if settings are uninitialized
When it happens
Trigger: Any code path — a unit test, a binary entry point, a background thread or lazy static — dereferences `settings::statics()` (or helpers that call it, like `ac()` pattern builders) before `settings::init()` has stored the value. Typical: a test that touches a module using `statics()` without calling `settings::init()` first, or a second binary/entry point missing the init call.
Common situations: New integration tests that import sensor modules but skip the init fixture; reordering startup so a consumer runs before init; spawning threads that read statics before the main thread initializes; adding a new binary target without copying the init sequence from main.
Related errors
- aho-corasick build
- forwarded-for regex
- one of the two matched
- just probed
- SUSPICIOUS_HTTP_REQUEST_REGEXES must carry a 'code…
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/ae30570d26fc0e9f.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/settings.rs:239
// It stays out of the trailing label above, where no real TLD has one.
b[..i - 1].iter().all(|c| c.is_ascii_alphanumeric() || *c == b'.' || *c == b'-' || *c == b'_')
}
/// `\A\d+\-\d+\-\d+\-\d+\Z`, hand-coded — the dashed-quad first label check.
#[inline]
pub fn is_dashed_quad(label: &str) -> bool {
let mut groups = 0;
for part in label.split('-') {
if part.is_empty() || !part.bytes().all(|c| c.is_ascii_digit()) {
return false;
}
groups += 1;
}
groups == 4
}
pub fn statics() -> &'static Statics {
STATICS.get().expect("settings::init() must run before statics()")
}
fn ac(patterns: &[&str]) -> AhoCorasick {
AhoCorasick::new(patterns).expect("aho-corasick build")
}
/// `ac()` for patterns that have to match regardless of case, which is how HTTP header names
/// arrive on the wire.
fn ac_nocase(patterns: &[&str]) -> AhoCorasick {
aho_corasick::AhoCorasickBuilder::new().ascii_case_insensitive(true).build(patterns).expect("aho-corasick build")
}
impl Statics {
pub fn build(root: PathBuf) -> Statics {
let ua_src = build_suspicious_ua_regex(&root);
let suspicious_ua = match pyre::build(&ua_src) {
Ok(re) => Some(re),
Err(e) => {View on GitHub (pinned to 77cfb06d76)