stamparm/maltrail · error · SystemExit
[!] version %r is not 'major.minor[.patch]'
Error message
[!] version %r is not 'major.minor[.patch]'
What it means
series() reduces a version string like '3.0.1' or '3.0' to a (major, minor) tuple so Maltrail's two-component versions can be compared with Cargo's three-component ones. It strips any pre-release/build suffix (after '-' or '+') and then requires at least major.minor. A version with fewer than two dot-separated components cannot be reduced and triggers this exit.
Solutions
- Use a version with at least major.minor, e.g. '3.0' or '3.0.1'
- Re-run with --tag '3.0' (or 'v3.0') instead of a major-only tag
- Check the version source (tag/CITATION.cff/Cargo.toml) for accidental truncation
- Remember pre-release suffixes like '3.0.0-rc1' are allowed — the issue is missing the minor component
Example fix
// before check_version.py --tag 3 // after check_version.py --tag 3.0
Defensive patterns
Strategy: validation
Validate before calling
import re
def is_valid_series(v):
v = re.split(r'[-+]', v, 1)[0]
parts = v.split('.')
return len(parts) >= 2 and parts[0].isdigit() and parts[1].isdigit() Prevention
- Always tag releases as major.minor or major.minor.patch
- Encode pre-releases as suffixes (-rc1) not alternate components
- Validate tags in CI before invoking check_version.py
When it happens
Trigger: Passing --tag or configuring a version like '3', 'v3' (after prefix stripping), or an empty-ish value so that after re.split(r'[-+]') and split('.') there are fewer than 2 parts.
Common situations: Tagging a release as just '3' instead of '3.0'; passing a major-only version on the CLI; a version source containing only one numeric component.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- [!] %r is not 'major.minor' - Maltrail versions two…
- [!] version %r has non-numeric components
- [!] no trail set at (pass --trails)
- [x] invalid IP address
- not a Maltrail provenance sidecar (bad magic)
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/c827edce3506ce78.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/tools/check_version.py:113
"""
match = re.search(r'name = "maltrail-sensor"\nversion = "([^"]+)"', _read(CARGO_LOCK))
if not match:
raise SystemExit("[!] no maltrail-sensor package entry found in %s" % CARGO_LOCK)
return match.group(1)
def series(version):
"""'3.0.1' and '3.0' both reduce to (3, 0) - Maltrail versions two components, Cargo three.
A SemVer pre-release / build suffix is stripped first, so 'v3.0-rc1' and '3.0.0+deb' are the
3.0 series like anything else. Release candidates are the whole point of having a release
pipeline that can be rehearsed, and refusing to name one would have made that impossible.
"""
version = re.split(r"[-+]", version, 1)[0]
parts = version.split('.')
if len(parts) < 2:
raise SystemExit("[!] version %r is not 'major.minor[.patch]'" % version)
try:
return (int(parts[0]), int(parts[1]))
except ValueError:
raise SystemExit("[!] version %r has non-numeric components" % version)
def main():
parser = argparse.ArgumentParser(description="check that the sensor, the server and CITATION.cff agree on the version")
parser.add_argument("--tag", help="also require both to match this release tag (e.g. '3.0' or 'v3.0')")
args = parser.parse_args()
settings, cargo, citation = settings_version(), cargo_version(), citation_version()
generated, locked = settings_gen_version(), cargo_lock_version()
print("[i] core/settings.py VERSION = %s" % settings)
print("[i] sensor/Cargo.toml version = %s" % cargo)
print("[i] CITATION.cff version = %s" % citation)
print("[i] settings_gen.rs VERSION = %s" % generated)
print("[i] Cargo.lock version = %s" % locked)View on GitHub (pinned to 77cfb06d76)