stamparm/maltrail · error

[!] not built - cargo build --release --manifest-path…

Error message

[!] %s not built - cargo build --release --manifest-path sensor/Cargo.toml

What it means

refnet.py's replay() drives the compiled sensor binary (default sensor/target/release/maltrail-sensor) against a generated pcap. Before running, it checks the binary exists; if it doesn't, replay exits telling you the exact cargo command to build it. This is a precondition check for the replay/scoring workflow, not a failure of replay itself.

Solutions

  1. Build the sensor: cargo build --release --manifest-path sensor/Cargo.toml
  2. If a custom path was passed, verify the file exists there or omit --sensor to use the default target/release path
  3. Re-run `cargo clean` victims by rebuilding after any clean or toolchain switch
  4. In CI, ensure the build step precedes the replay step

Example fix

# before
python sensor/tools/refnet.py replay ...   # binary missing
# after
cargo build --release --manifest-path sensor/Cargo.toml
python sensor/tools/refnet.py replay ...
Defensive patterns

Strategy: validation

Validate before calling

import os
sensor = "sensor/target/release/maltrail-sensor"
assert os.path.isfile(sensor), "run: cargo build --release --manifest-path sensor/Cargo.toml"

Prevention

When it happens

Trigger: Running refnet.py replay (via main, without --score) when sensor/target/release/maltrail-sensor does not exist — the project was never built with cargo build --release, a `cargo clean` was run, or a custom --sensor path points to a non-existent file.

Common situations: Fresh clone without building; switching toolchains/target dirs; CI running the replay step before the build step; typos in a custom sensor path.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/d8466620f727cc72. Report an issue: GitHub.

Appendix: source

Thrown at sensor/tools/refnet.py:224

    false = [_ for _ in seen if _ not in planted and _ not in explained and _[0] not in scanners]

    hours = meta["minutes"] / 60.0
    per_1000_day = (len(logged) / max(hours, 1e-9)) * 24.0 * (1000.0 / meta["hosts"])
    return {
        "planted": len(planted), "detected": len(detected), "missed": sorted(missed)[:10],
        "scans_planted": len(scanners), "scans_detected": len(scans_found),
        "false_positives": len(false), "false_examples": sorted(false)[:10],
        "benign_packets": meta["benign_packets"], "events": len(logged),
        "detection_rate": 100.0 * len(detected) / max(len(planted), 1),
        "fp_per_100k_benign": 100000.0 * len(false) / max(meta["benign_packets"], 1),
        "events_per_day_per_1000_hosts": per_1000_day,
    }


def replay(pcap, out, trails, sensor=None):
    sensor = sensor or os.path.join(ROOT, "sensor", "target", "release", "maltrail-sensor")
    if not os.path.isfile(sensor):
        raise SystemExit("[!] %s not built - cargo build --release --manifest-path sensor/Cargo.toml" % sensor)
    logdir = os.path.join(out, "logs")
    if not os.path.isdir(logdir):
        os.makedirs(logdir)
    conf = os.path.join(out, "sensor.conf")
    with io.open(conf, "w", encoding="utf8") as handle:
        # USE_HEURISTICS EXPLICITLY. An absent key reads as false, so a config that simply omits it
        # runs with every heuristic off - and the first version of this file did exactly that, then
        # reported 0/3 scans detected as though the sensor had missed them. A reference network that
        # forgets to turn on what it is measuring produces confident, meaningless numbers.
        handle.write(u"MONITOR_INTERFACE any\nCAPTURE_BUFFER 10MB\nSENSOR_NAME refnet\n"
                     u"USE_HEURISTICS true\n"
                     u"DISABLE_CHECK_SUDO true\nDISABLE_TRAIL_UPDATES true\nUSE_SERVER_UPDATE_TRAILS false\n"
                     u"UPDATE_PERIOD 86400\nLOG_DIR %s\nTRAILS_FILE %s\n" % (logdir, trails))
    started = time.time()
    subprocess.check_call([sensor, "-c", conf, "-r", pcap], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    return logdir, time.time() - started

View on GitHub (pinned to 77cfb06d76)