{"record":{"id":"ae30570d26fc0e9f","repo":"stamparm/maltrail","slug":"settings-init-must-run-before-statics","errorCode":null,"errorMessage":"settings::init() must run before statics()","messagePattern":"settings::init\\(\\) must run before statics\\(\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sensor/src/settings.rs","lineNumber":239,"sourceCode":"    // It stays out of the trailing label above, where no real TLD has one.\n    b[..i - 1].iter().all(|c| c.is_ascii_alphanumeric() || *c == b'.' || *c == b'-' || *c == b'_')\n}\n\n/// `\\A\\d+\\-\\d+\\-\\d+\\-\\d+\\Z`, hand-coded — the dashed-quad first label check.\n#[inline]\npub fn is_dashed_quad(label: &str) -> bool {\n    let mut groups = 0;\n    for part in label.split('-') {\n        if part.is_empty() || !part.bytes().all(|c| c.is_ascii_digit()) {\n            return false;\n        }\n        groups += 1;\n    }\n    groups == 4\n}\n\npub fn statics() -> &'static Statics {\n    STATICS.get().expect(\"settings::init() must run before statics()\")\n}\n\nfn ac(patterns: &[&str]) -> AhoCorasick {\n    AhoCorasick::new(patterns).expect(\"aho-corasick build\")\n}\n\n/// `ac()` for patterns that have to match regardless of case, which is how HTTP header names\n/// arrive on the wire.\nfn ac_nocase(patterns: &[&str]) -> AhoCorasick {\n    aho_corasick::AhoCorasickBuilder::new().ascii_case_insensitive(true).build(patterns).expect(\"aho-corasick build\")\n}\n\nimpl Statics {\n    pub fn build(root: PathBuf) -> Statics {\n        let ua_src = build_suspicious_ua_regex(&root);\n        let suspicious_ua = match pyre::build(&ua_src) {\n            Ok(re) => Some(re),\n            Err(e) => {","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/src/settings.rs#L221-L257","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet statics = settings::statics(); // panics if init not yet run\n// after\nsettings::init(&config).expect(\"settings init failed\");\nlet statics = settings::statics();","handlingStrategy":"type-guard","validationCode":"// caller-side pre-check\nfn statics_ready() -> bool { settings::initialized() } // or STATICS.get().is_some()","typeGuard":"fn try_statics() -> Option<&'static Statics> { settings::try_statics() }","tryCatchPattern":"// std::sync::OnceLock based\nlet statics = STATICS.get().unwrap_or_else(||\n    panic!(\"settings::init() must run before statics(); call it in main()/test setup\"));","preventionTips":["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"],"tags":["rust","initialization","panic","settings","static"],"backgroundTag":"module-init-failed","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}