stamparm/maltrail · error
just probed
Error message
just probed
What it means
In the HTTP request host-based trail lookup, the code calls `st.trails.db().get(&candidate)` twice: once with `is_some()` to test, then again with `.expect("just probed")` to unwrap. If the two lookups ever disagree — or the first check passes but the second returns `None` — the `expect` panics. This is a redundant double-lookup pattern whose invariant ('we just probed it, so it must still be there') is asserted rather than guaranteed.
Solutions
- Replace the check-then-unwrap double lookup with a single `if let Some(hit) = st.trails.db().get(&candidate)` that uses the returned reference directly — removes the invariant entirely.
- If cloning is needed, clone `info`/`reference` from the single `get` result instead of re-querying.
- Ensure the trails DB handle returned by `db()` is stable across calls within one request, or snapshot it once per request.
- Add a concurrency test that reloads the DB while requests are parsed to prove no panic path remains.
Example fix
// before
if st.trails.db().get(&candidate).is_some() {
let hit = st.trails.db().get(&candidate).expect("just probed");
let (info, reference) = (hit.info.to_string(), hit.reference.to_string());
...
}
// after
if let Some(hit) = st.trails.db().get(&candidate) {
let (info, reference) = (hit.info.to_string(), hit.reference.to_string());
...
} Defensive patterns
Strategy: validation
Validate before calling
// single lookup; no second get() can disagree
if let Some(hit) = st.trails.db().get(&candidate) { /* use hit */ } Try / catch
catch_unwind(AssertUnwindSafe(|| handle_http_host(st, ep)))
.unwrap_or_else(|_| log::warn!("http host lookup panicked; request skipped")); Prevention
- Never check-then-requery: bind the result of the first get()
- Treat db().get() as possibly non-idempotent under reload
- Snapshot the DB handle once per request
- Fuzz/concurrency-test with concurrent DB reloads
When it happens
Trigger: An HTTP request whose `host + '/'` key is reported present by the first `db().get()` but absent on the immediate second `db().get()`, panicking at `.expect("just probed")`. Any non-deterministic `get` (concurrent DB reload/swap between the two calls, `get` with side effects or interior mutability) triggers it.
Common situations: Trails DB hot-reload racing request handling; a custom `db()` implementation whose `get` is not pure; copy-paste refactors where the boolean check and the unwrap query different keys.
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
- one of the two matched
- question
- hmac accepts any key length
- settings::init() must run before statics()
- aho-corasick build
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/95b1bc63fdc604db.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/process.rs:1137
} else {
// parts = url.split(check); every non-empty part is bracketed
let url = match &prebuilt_url {
Some(u) => Cow::Borrowed(u.as_str()),
None => Cow::Owned(format!("{host}{path}")),
};
let trail = bracket_around(&url, &candidate);
emit_ep(st, sec, usec, ep, PROTO::TCP, TRAIL::URL, Field::Text(trail), &info, &reference);
}
return;
}
}
// `format!("{host}/")` allocated on every request; the candidate buffer is already sized.
candidate.clear();
candidate.push_str(&host);
candidate.push('/');
if st.trails.db().get(&candidate).is_some() {
let hit = st.trails.db().get(&candidate).expect("just probed");
let (info, reference) = (hit.info.to_string(), hit.reference.to_string());
emit_ep(st, sec, usec, ep, PROTO::TCP, TRAIL::URL, Field::Text(candidate.clone()), &info, &reference);
return;
}
if !st.cfg.use_heuristics || heuristics_suppressed {
return;
}
// Forwarded-for headers are searched in the RAW packet bytes, case-insensitively. The
// literal pre-condition comes first: asking a case-insensitive alternation for capture
// groups walks the whole packet, and next to no request carries one of these headers.
let mut src_ip_field = ep.src.render().as_str().to_string();
if st.statics.forwarded_for_pre_condition.is_match(packet_bytes) {
if let Some(caps) = st.statics.forwarded_for.captures(packet_bytes) {
if let Some(m) = caps.get(2) {
let forwarded = String::from_utf8_lossy(m.as_bytes()).to_string();
src_ip_field = format!("{src_ip_field},{forwarded}");View on GitHub (pinned to 77cfb06d76)