stamparm/maltrail · error · SystemExit
[!] version %r has non-numeric components
Error message
[!] version %r has non-numeric components
What it means
series() converts the first two dot-separated components of a version to integers. If either component is non-numeric (e.g. '3.x', 'latest', or letters in the major/minor slot), int() raises ValueError and the tool exits with this message. Pre-release/build suffixes after '-' or '+' are already stripped, so this only fires for genuinely non-numeric major/minor parts.
Solutions
- Correct the version in the offending source (CITATION.cff, Cargo.toml/lock, settings mirror) to numeric major.minor[.patch]
- Use pre-release suffixes instead of letters in components: '3.0.0-rc1' rather than '3.rc1'
- Grep the version sources for non-numeric components before running the checker
- Re-run check_version.py to verify the fix
Example fix
// before (CITATION.cff) version: "3.rc1" // after version: "3.0.0-rc1"
Defensive patterns
Strategy: validation
Validate before calling
import re
def is_numeric_version(v):
v = re.split(r'[-+]', v, 1)[0]
parts = v.split('.')
return len(parts) >= 2 and all(p.isdigit() for p in parts[:2]) Prevention
- Never put placeholder text like 'latest' in version fields
- Use semver pre-release suffixes instead of letters in numeric slots
- Lint version files for numeric components before releases
When it happens
Trigger: A version string whose major or minor component is not an integer after stripping any [-+] suffix — e.g. 'three.0', '3.x', '3.0.dev' is fine (suffix) but '3.dev.0' is not — when passed via --tag or read from a version file.
Common situations: Placeholder or template text left in a version file ('latest', 'X.Y'); a typo like '3,O' in Cargo.toml or CITATION.cff; semver2 variants with letters in the numeric positions.
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
- [!] version %r is not 'major.minor[.patch]'
- [x] invalid IP address
- not a Maltrail provenance sidecar (bad magic)
- provenance sidecar is truncated
- packet too short for header-protection sample
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/e2d7026c70d74e1d.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/tools/check_version.py:117
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)
if series(settings) != series(cargo):
print("[x] the server and the sensor would report different versions", file=sys.stderr)
print("[?] make sensor/Cargo.toml '%s.0' or core/settings.py '%d.%d'"View on GitHub (pinned to 77cfb06d76)