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
- Inspect the named file and restore/fix exactly one version line matching the script's pattern for that file.
- 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.
- 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
- Keep exactly one version line per tracked file in the exact expected format
- When upgrading upstream Maltrail files, re-check the version-line format still matches the script
- Avoid adding dependency version pins that mimic the tracked version-line pattern
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
- [!] no VERSION assignment in core/settings.py
- [!] : expected exactly 1 date-released line, matched
- [!] cannot bump %r - expected 'major.minor'
- [!] %r is not 'major.minor' - Maltrail versions two…
- [!] no VERSION assignment found in
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)