stamparm/maltrail · error · SystemExit

[!] : expected exactly 1 version line, matched Refusing to…

Error message

[!] %s: expected exactly 1 version line, matched %d
    Refusing to write ANY file - a half-applied bump is the failure this script exists to prevent.

What it means

apply() rewrites each known version-containing file by substituting its version line with re.subn and requires exactly one replacement per file. Zero matches means the file no longer matches the pattern (stale mapping), and >1 means the pattern hit multiple lines (e.g. a dependency's version key). In either case it aborts BEFORE writing any file, preserving the all-or-nothing guarantee.

Solutions

  1. Inspect the named file and restore/fix exactly one version line matching the script's pattern for that file.
  2. If a dependency's version line collides with the pattern, make the pattern in apply() more specific (e.g. anchor to the variable name) or change the offending line's format.
  3. If the file legitimately no longer carries a version, update the script's FILES table to drop it rather than forcing the bump.

Example fix

// before
VERSION = "0.53"
DEPENDENCY_VERSION = "0.53"  # second match -> n != 1
// after
VERSION = "0.54"
DEPENDENCY_VERSION = "1.2.0"  # distinct format, no collision
Defensive patterns

Strategy: validation

Validate before calling

import re
for path, pattern in bump_version.FILES:
    n = len(re.findall(pattern, open(path).read(), re.M))
    if n != 1:
        print(f'{path}: version pattern matched {n} times - fix before bumping')

Try / catch

import subprocess
p = subprocess.run(['python', 'sensor/tools/bump_version.py', '--next'])
if p.returncode != 0:
    # apply() aborts before writing anything; repo is unchanged, fix files and retry
    verify_no_files_modified()

Prevention

When it happens

Trigger: Running a bump when a tracked file's version line was renamed/removed (n=0), or when the version regex accidentally matches more than one line in a file (n>1), such as a pinned dependency version sharing the same line format.

Common situations: Upstream Maltrail files were updated and their version line changed shape; a new file or dependency block was added containing a second line matching the pattern; a previous partial edit left duplicate version lines.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13). Data as JSON: /api/errors/271281be9a02af58. Report an issue: GitHub.

Appendix: source

Thrown at sensor/tools/bump_version.py:94

def next_minor(version):
    parts = version.split(".")
    if len(parts) < 2 or not all(p.isdigit() for p in parts[:2]):
        raise SystemExit("[!] cannot bump %r - expected 'major.minor'" % version)
    return "%s.%d" % (parts[0], int(parts[1]) + 1)


def apply(version, date_released=None):
    """Rewrite every file, or none of them."""

    planned = []
    for path, pattern, repl in _edits(version):
        text = _read(path)
        new, n = re.subn(pattern, repl, text, flags=re.M)
        # Exactly once. Zero means the file moved on and this script is now lying about
        # covering it; more than once means the pattern is catching something it should not
        # (a dependency's version key, say) and would corrupt the file.
        if n != 1:
            raise SystemExit("[!] %s: expected exactly 1 version line, matched %d\n"
                             "    Refusing to write ANY file - a half-applied bump is the "
                             "failure this script exists to prevent." % (path, n))
        planned.append((path, new, text != new))

    if date_released:
        path = os.path.join(ROOT, "CITATION.cff")
        for i, (p, new, _) in enumerate(planned):
            if p != path:
                continue
            out, n = re.subn(r'^(date-released\s*:\s*")[^"]*(")',
                             r'\g<1>%s\g<2>' % date_released, new, flags=re.M)
            if n != 1:
                raise SystemExit("[!] %s: expected exactly 1 date-released line, matched %d" % (p, n))
            planned[i] = (p, out, out != _read(p))

    for path, new, changed in planned:
        if changed:
            with io.open(path, "w", encoding="utf8") as f:

View on GitHub (pinned to 77cfb06d76)