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

  1. 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).
  2. Check that the version constant wasn't renamed during a refactor and revert to the VERSION name expected by check_version.py.
  3. Confirm the SETTINGS constant in check_version.py points at the actual core/settings.py path if the project layout changed.
  4. 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

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


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)