stamparm/maltrail · error · SystemExit
[!] no VERSION assignment found in
Error message
[!] no VERSION assignment found in %s
What it means
check_version.py's settings_version() reads core/settings.py as text and regex-extracts a top-level VERSION = "x.y" assignment (avoiding importing the module, which has side effects). If the regex ^VERSION\s*=\s*["']([^"']+)["'] finds no match, it raises SystemExit telling the developer no VERSION assignment exists in that file — the script's first source of truth for the project version is missing or malformed.
Solutions
- Add or restore a line-level assignment in core/settings.py exactly like VERSION = "3.0" (name at start of line, = sign, single- or double-quoted string literal).
- Check that the version constant wasn't renamed during a refactor and revert to the VERSION name expected by check_version.py.
- Confirm the SETTINGS constant in check_version.py points at the actual core/settings.py path if the project layout changed.
- Avoid computed or annotated version declarations (VERSION: str = ... or VERSION = compute()); keep a plain literal for the checker to find.
Example fix
# before (settings.py) APP_VERSION = "3.0" # after VERSION = "3.0"
Defensive patterns
Strategy: validation
Validate before calling
import re
text = open("sensor/core/settings.py").read()
assert re.search(r'^VERSION\s*=\s*["\'][^"\']+["\']', text, re.M), \
"core/settings.py must contain a literal VERSION assignment before running check_version" Prevention
- Keep VERSION as a plain, line-leading string literal in core/settings.py; never rename it during refactors without grepping for dependents.
- Add a unit test that parses settings.py with the same regex and fails fast if the assignment disappears.
- Avoid computed or annotated version declarations (VERSION: str = ..., VERSION = compute()).
- If the project layout changes, update the SETTINGS path constant in check_version.py in the same commit.
When it happens
Trigger: core/settings.py no longer contains a line starting with VERSION = (or VERSION='...') at line start; the assignment was renamed (e.g. APP_VERSION), quoted with backticks, written as VERSION: str = "3.0", built by concatenation, or the file path constant SETTINGS points to the wrong/missing file (though a missing file may instead fail in _read).
Common situations: A refactor of settings.py renamed or reformatted the version constant; a contributor replaced the literal with a computed value; the check tool was moved and SETTINGS now resolves to a different file after a layout change.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- [!] no VERSION assignment in core/settings.py
- [!] cannot bump %r - expected 'major.minor'
- [!] : expected exactly 1 version line, matched Refusing to…
- [!] : expected exactly 1 date-released line, matched
- [!] %r is not 'major.minor' - Maltrail versions two…
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/5e588f5fbb26d49f.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/tools/check_version.py:44
ROOT = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".."))
SETTINGS = os.path.join(ROOT, "core", "settings.py")
CARGO = os.path.join(ROOT, "sensor", "Cargo.toml")
CITATION = os.path.join(ROOT, "CITATION.cff")
SETTINGS_GEN = os.path.join(ROOT, "sensor", "src", "settings_gen.rs")
CARGO_LOCK = os.path.join(ROOT, "sensor", "Cargo.lock")
def _read(path):
with open(path, "r") as f:
return f.read()
def settings_version():
"""VERSION = "3.0" from core/settings.py, without importing it (it has side effects)."""
match = re.search(r"^VERSION\s*=\s*[\"']([^\"']+)[\"']", _read(SETTINGS), re.M)
if not match:
raise SystemExit("[!] no VERSION assignment found in %s" % SETTINGS)
return match.group(1)
def cargo_version():
"""version = "3.0.0" from the [package] table only - dependency versions must not match."""
text = _read(CARGO)
package = re.search(r"^\[package\]\s*$(.*?)(?=^\[|\Z)", text, re.M | re.S)
if not package:
raise SystemExit("[!] no [package] table found in %s" % CARGO)
match = re.search(r"^version\s*=\s*\"([^\"]+)\"", package.group(1), re.M)
if not match:
raise SystemExit("[!] no version key in the [package] table of %s" % CARGO)
return match.group(1)
def citation_version():
"""version: "3.0" from CITATION.cff.
View on GitHub (pinned to 77cfb06d76)