stamparm/maltrail · error

[!] no trail set at (pass --trails)

Error message

[!] no trail set at %s (pass --trails)

What it means

refnet.py main() requires a trail set file to drive pcap generation. When not in --score mode it checks that the --trails path exists as a file; if not, it exits with this message reminding you to pass --trails. Without trails there is nothing to plant in the generated traffic, so the tool refuses to continue.

Solutions

  1. Pass --trails with the path to an existing trail file, e.g. --trails trails.csv
  2. Verify the path exists (ls the file) and that you're running from the directory your relative path assumes
  3. Copy or download the trail set if it lives outside this checkout
  4. If you meant to score a previous run instead, use --score with the run's output directory

Example fix

# before
python sensor/tools/refnet.py --out /tmp/run
# after
python sensor/tools/refnet.py --out /tmp/run --trails trails/trails.csv
Defensive patterns

Strategy: validation

Validate before calling

import os, argparse
opts = parser.parse_args()
assert opts.trails and os.path.isfile(opts.trails), f"trail set missing at {opts.trails!r} - pass --trails"

Prevention

When it happens

Trigger: Running refnet.py (generate mode, no --score) with the --trails option pointing to a non-existent file, or relying on a default trails path that isn't present at the expected location.

Common situations: Forgot to pass --trails on the CLI; passed a relative path from the wrong working directory; trails file was deleted/moved or lives in a private feed directory not present on this machine.

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/46e385f14348ab8a. Report an issue: GitHub.

Appendix: source

Thrown at sensor/tools/refnet.py:264

    parser.add_argument("--hosts", type=int, default=500)
    parser.add_argument("--minutes", type=int, default=60)
    parser.add_argument("--seed", type=int, default=1312)
    parser.add_argument("--planted", type=int, default=10, help="per trail kind")
    parser.add_argument("--scans", type=int, default=3)
    parser.add_argument("--trails", default=os.path.expanduser("~/.maltrail/trails.csv"))
    parser.add_argument("--sensor", default=None)
    parser.add_argument("--generate", action="store_true", help="write the pcap and stop")
    parser.add_argument("--score", metavar="DIR", help="score a directory a previous run wrote")
    parser.add_argument("--min-detection", type=float, default=None, help="fail below this %%")
    parser.add_argument("--max-false", type=int, default=None, help="fail above this many false positives")
    options = parser.parse_args()

    if options.score:
        meta = json.load(io.open(os.path.join(options.score, "truth.json"), encoding="utf8"))
        result = score(meta, events(os.path.join(options.score, "logs")))
    else:
        if not os.path.isfile(options.trails):
            raise SystemExit("[!] no trail set at %s (pass --trails)" % options.trails)
        pcap, meta = generate(options.out, options.hosts, options.minutes, options.seed,
                              options.planted, options.scans, options.trails)
        print("[i] %d host(s), %d minute(s), seed %d -> %d packets (%.0f MB)"
              % (meta["hosts"], meta["minutes"], meta["seed"], meta["packets"], os.path.getsize(pcap) / 1e6))
        if options.generate:
            return 0
        logdir, elapsed = replay(pcap, options.out, options.trails, options.sensor)
        print("[i] replayed in %.2fs" % elapsed)
        result = score(meta, events(logdir))

    print("\n[i] detection rate            %5.1f%%  (%d/%d planted trails)"
          % (result["detection_rate"], result["detected"], result["planted"]))
    print("[i] scans detected            %5d/%d" % (result["scans_detected"], result["scans_planted"]))
    print("[i] false positives           %5d   (%.2f per 100k benign packets)"
          % (result["false_positives"], result["fp_per_100k_benign"]))
    print("[i] events/day/1000 hosts     %5.0f" % result["events_per_day_per_1000_hosts"])
    if result["missed"]:
        print("\n[!] missed: %s" % ", ".join("%s->%s" % _ for _ in result["missed"][:5]))

View on GitHub (pinned to 77cfb06d76)