stamparm/maltrail · error
just inserted
Error message
just inserted
What it means
Internal invariant assertion in DnsExhaustion::observe: after the HEURISTIC_MAX_KEYS guard, the code inserts a fresh Window for `domain` and immediately retrieves it with get_mut().expect("just inserted"). Failure is impossible under normal concurrency (the whole observe body is behind the same lock), so a panic means the map was mutated between insert and get or the insert silently failed.
Solutions
- Verify the insert and get_mut still happen under the same lock scope with no intervening mutation of self.domains
- Replace insert+get_mut with the entry() API, which cannot miss
- Re-check the HEURISTIC_MAX_KEYS guard still returns before the insert path is reached
Example fix
// before
self.domains.insert(domain.into(), Window { start: sec, subdomains: StrSet::default() });
self.domains.get_mut(domain).expect("just inserted")
// after
match self.domains.entry(domain.into()) {
std::collections::hash_map::Entry::Vacant(v) => v.insert(Window { start: sec, subdomains: StrSet::default() }),
std::collections::hash_map::Entry::Occupied(o) => o.into_mut(),
} Defensive patterns
Strategy: type-guard
Type guard
fn window_mut<'m>(m: &'m mut HashMap<String, Window>, k: &str, sec: u64) -> &'m mut Window {
m.entry(k.to_owned()).or_insert(Window { start: sec, subdomains: StrSet::default() })
} Prevention
- Use entry()/or_insert instead of insert+get_mut
- Keep insert and lookup in one lock scope
- Never add eviction inside the insert-then-use window
When it happens
Trigger: The domain entry is removed or overwritten between insert and get_mut — e.g. code added later that clears/retains self.domains inside the same locked section, or a refactor that swaps insert for entry-API misuse.
Common situations: Refactoring observe() (e.g. introducing an eviction pass or a second lock scope) that breaks the insert-then-get invariant; never triggered by DNS traffic itself.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- just inserted
- [!] no VERSION constant found in
- [!] no maltrail-sensor package entry found in
- [!] not built - cargo build --release --manifest-path…
- invalid USERS entry ' ' [?] (hint: add whitespace at start…
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/47aab83ad9f3f5a9.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/heuristics/dns_exhaustion.rs:81
}
/// Record `subdomain_part` under `domain` and report what the caller should do.
/// `threshold` is passed in so tests can lower it, exactly like the Python test does.
pub fn observe(&mut self, domain: &str, subdomain_part: &str, sec: u64, threshold: usize) -> Outcome {
// NOTE: membership test, not truthiness - an existing-but-empty set (just cleared
// at the 60s boundary) must keep its window start.
let window = match self.domains.get_mut(domain) {
Some(w) => w,
None => {
// Bounded: the hourly reset is a TIME bound, and the parent domain is chosen by
// whoever sends the query. Refuse new domains at the cap instead of evicting
// tracked ones, so a flood cannot push an in-progress window out of memory.
if self.domains.len() >= super::HEURISTIC_MAX_KEYS {
self.saturations += 1;
return Outcome::Continue;
}
self.domains.insert(domain.into(), Window { start: sec, subdomains: StrSet::default() });
self.domains.get_mut(domain).expect("just inserted")
}
};
if sec.saturating_sub(window.start) > 60 {
window.start = sec;
window.subdomains.clear();
return Outcome::Continue;
}
if window.subdomains.len() < threshold {
// `contains` first: `insert(subdomain_part.into())` would build an owned copy of the
// label on EVERY query just to throw it away as a duplicate. Real traffic queries the
// same handful of subdomains over and over, so that was an allocation per DNS packet.
if !window.subdomains.contains(subdomain_part) {
window.subdomains.insert(subdomain_part.into());
}
return Outcome::Continue;
}
if !self.exhausted.contains(domain) {View on GitHub (pinned to 77cfb06d76)