GyulyVGC/sniffnet · error · SystemExit

Cannot parse debian/changelog

Error message

Cannot parse debian/changelog

What it means

prepare_staged() reads resources/packaging/linux/deb-source/debian/changelog and parses its first line with the regex ^sniffnet (<version>) <distribution>;. If the first line does not match this exact Debian changelog format, the version cannot be determined and the script aborts. The parser expects the standard Debian changelog header and does not tolerate deviating package names or missing fields.

Solutions

  1. Inspect the first line of resources/packaging/linux/deb-source/debian/changelog and fix it to match: sniffnet (<version>) <distro>; e.g. 'sniffnet (1.3.1-1) trixie;'
  2. Restore the changelog from git (git checkout -- resources/packaging/linux/deb-source/debian/changelog)
  3. If adding a new entry, use dch or copy an existing entry's header format exactly, keeping 'sniffnet', parentheses, and the trailing semicolon

Example fix

# before
sniffnet 1.3.1 trixie;
# after
sniffnet (1.3.1) trixie;
Defensive patterns

Strategy: validation

Validate before calling

import re
first = open("resources/packaging/linux/deb-source/debian/changelog").readline()
if not re.match(r"sniffnet \(([^)]+)\) ([\w-]+);", first):
    raise SystemExit("debian/changelog header is malformed")

Type guard

def changelog_header_is_valid(text: str) -> bool:
    return re.match(r"sniffnet \(([^)]+)\) ([\w-]+);", text) is not None

Try / catch

try:
    prepare(output)
except SystemExit as e:
    if "Cannot parse debian/changelog" in str(e):
        print("Fix debian/changelog first line to: sniffnet (<version>) <distro>;", file=sys.stderr)
    raise

Prevention

When it happens

Trigger: Running prepare.py when the first line of debian/changelog is malformed: package name other than 'sniffnet' at line start, missing parenthesized version, missing distribution codename, missing trailing semicolon, or a changelog starting with blank lines or a comment.

Common situations: Hand-editing the changelog and breaking the header; renaming the package; an automated tool reformatting the changelog; a changelog file accidentally emptied or replaced with a template; missing closing semicolon after the distribution name.

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


AI-assisted analysis of GyulyVGC/sniffnet@592a62a5ad (2026-09-18). Data as JSON: /api/errors/c8b70c964e57403a. Report an issue: GitHub.

Appendix: source

Thrown at resources/packaging/linux/deb-source/prepare.py:43

def prepare(output):
    if output.exists() and any(output.iterdir()):
        raise SystemExit(f"Refusing to overwrite an existing release in {output}; use --output with a fresh directory")
    # Stage outside the checkout so Cargo cannot inherit a newer parent workspace.
    with tempfile.TemporaryDirectory(prefix="sniffnet-debian-") as staging:
        source = prepare_staged(Path(staging))
        output.mkdir(parents=True, exist_ok=True)
        for path in Path(staging).iterdir():
            shutil.move(str(path), output / path.name)
    print(f"Release files: {output}", flush=True)
    return output / source.name


def prepare_staged(output):
    changelog = (DEBIAN / "changelog").read_text()
    match = re.match(r"sniffnet \(([^)]+)\) ([\w-]+);", changelog)
    if not match:
        raise SystemExit("Cannot parse debian/changelog")
    version = match[1].rsplit("-", 1)[0]
    tag = f"refs/tags/v{version}"
    commit = run("git", "rev-parse", "--verify", f"{tag}^{{commit}}",
                 capture_output=True, text=True).stdout.strip()
    manifest = tomllib.loads(run("git", "show", f"{commit}:Cargo.toml",
                                capture_output=True, text=True).stdout)
    if manifest["package"]["version"] != version:
        raise SystemExit("debian/changelog must match the tagged Cargo.toml version")
    source = output / f"sniffnet-{version}"
    archive = output / f"sniffnet_{version}.orig.tar.gz"
    if source.exists() or archive.exists():
        raise SystemExit(f"Refusing to overwrite an existing release in {output}; use --output with a fresh directory")
    epoch = int(run("git", "show", "-s", "--format=%ct", commit,
                    capture_output=True, text=True).stdout)
    output.mkdir(parents=True, exist_ok=True)
    source.mkdir()

    # Extract only release-tag inputs. A branch with the same name cannot win

View on GitHub (pinned to 592a62a5ad)