Skip to main content

EnvFixer

PyPI version Python versions CI License: MIT

Root-cause diagnosis for "passes locally, fails in CI."

EnvFixer captures a privacy-safe fingerprint of a Python environment (interpreter, OS/arch/libc, installed packages, and environment-variable names), compares two of them, and ranks the most likely cause of an environment-drift failure — then prints a plain-English fix for each.

Every other tool in this space stops at "here's the diff." EnvFixer tells you which difference is probably the cause, and how to fix it.

$ envfixer diff local.json ci.json

EnvFixer — diagnosis
=====================

3 candidate cause(s), most likely first:

1. [HIGH  ]  82/100 ████████████████····  package_missing: pydantic
      working: '2.4.2'   failing: None
      why: A package present where the code works is entirely absent where it
           fails; a missing import is the single most common CI failure.
      fix: Install 'pydantic' in the failing environment — add
           'pydantic==2.4.2' to your requirements/lockfile so CI installs it.

2. [MEDIUM]  58/100 ████████████········  python_version: python
      working: '3.11.5'   failing: '3.9.18'
      ...
  • Zero runtime dependencies. The core is pure standard library, so it runs inside any minimal CI container.
  • Privacy-safe by construction. It records environment-variable names only — never values. The fingerprint is plain JSON you can eyeball.
  • A CI guardrail, not just a report. Exit codes and --fail-on let it gate a build when environment drift is the likely culprit.

Install

pip install envfixer

Quick start

Capture a fingerprint inside the environment you want to snapshot (run it in the target virtualenv, e.g. python -m envfixer capture, so you fingerprint the right interpreter):

# on your machine
python -m envfixer capture -o local.json

# in CI
python -m envfixer capture -o ci.json

Diagnose the difference (first argument = where it works, second = where it fails):

envfixer diff local.json ci.json

Have the failing traceback? Pass it — differences it names are boosted to near-certainty:

envfixer diff local.json ci.json --traceback ci-error.log

Health-check a single environment (no second fingerprint needed) — finds missing dependencies and version conflicts, each with a fix:

envfixer check

No EnvFixer in the failing environment? Diagnose it from its logs. Any pip freeze output works as an input, so you never have to install anything in the broken container:

envfixer capture --from-freeze ci-freeze.txt --python-version 3.9.18 -o ci.json
envfixer diff local.json ci.json

Anything the dump cannot tell us (OS, env vars, timezone…) stays unknown and is skipped when diffing — a partial fingerprint never invents a difference.

Commands

Command What it does
capture [-o FILE] Write this environment's fingerprint (stdout by default).
capture --from-freeze FILE Build a fingerprint from a pip freeze/requirements dump (- = stdin).
diff WORKING FAILING [--traceback FILE] Rank the likely causes of the failure.
check [FINGERPRINT] Surface missing dependencies and version conflicts in one environment.

Shared reporting flags on diff/check:

  • --format {human,json,markdown,sarif} — markdown for PR comments, sarif for inline GitHub code-scanning annotations.
  • --fail-on {none,low,medium,high} — exit non-zero when a cause of at least this confidence is found (default high).
  • --top N — show only the top N results.

Use in CI (GitHub Actions)

- uses: actions/checkout@v4
- uses: actions/setup-python@v5
  with: { python-version: '3.11' }
- run: pip install envfixer
- run: envfixer capture -o ci.json
# compare against a committed baseline captured locally
- run: envfixer diff local.json ci.json --format markdown --fail-on high

A reusable composite action lives in .github/actions/envfixer.

How the ranking works

The ranking is a transparent rule system, not a black box — every score comes with a human-readable rationale. In brief:

  • Missing package (present where it works, absent where it fails) scores highest — it's the most common real cause.
  • Version mismatches are scored by blast radius (major > minor > patch).
  • Python / libc / arch differences are scored by how often they actually break code (e.g. glibc-vs-musl is weighted heavily).
  • Timezone and locale/encoding drift is scored too — the UTC-CI date bug and the ASCII-locale UnicodeEncodeError are real causes no lockfile sees.
  • Env-var names are filtered by significance: DATABASE_URL, *_TOKEN, AWS_*, PYTHONPATH score high; shell noise like SHLVL/TERM is dropped.
  • A provided traceback strongly boosts any difference it names.

See envfixer/rank.py for the exact rules.

Evaluation

Because "ranks by likely cause" is an empirical claim, EnvFixer ships a benchmark that measures it against both a synthetic fault injector and a curated real-world corpus (missing test plugins, urllib3 v2, Python 3.12 dropping distutils, Alpine/musl wheel gaps, NumPy 2.0 ABI breaks, UTC timezone drift, ASCII-locale encoding errors, …). On the current corpus (23 labelled cases): Top-1 100%, MRR 1.000, 0 false positives. See EVALUATION.md.

python -m evaluation.benchmark

Privacy

EnvFixer never reads environment-variable values, only their names. The fingerprint is human-readable JSON; inspect it before sharing.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. Good first contributions: new fault scenarios in evaluation/generate.py and new remedies in envfixer/fixes.py.

License

MIT — see LICENSE.

Metadata

Release files for envfixer 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for envfixer 0.2.0
File Size Uploaded
envfixer-0.2.0.tar.gz 30.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for envfixer 0.2.0
File Interpreter ABI Platform
envfixer-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 56.7 kB

Release files / envfixer-0.2.0.tar.gz

Download URL envfixer-0.2.0.tar.gz
Size 30.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b63fc8a0811e63dfe96444c6f22fb3b2bc32211e65add587e6d7f946ab7e67d4
BLAKE2b-256 checksum
How to use checksums
beb9b55d8da9dce54f06188ddd0d92c0c5ec974033fd7cc4f0619750816def44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 1, 2026.

Transparency log

Release files / envfixer-0.2.0-py3-none-any.whl

Download URL envfixer-0.2.0-py3-none-any.whl
Size 26.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
48637a31144625a2e19f1032032d238de4fce3fe1b7b259f8f988f9abe7a5349
BLAKE2b-256 checksum
How to use checksums
9c76e3875311672ab233da561f8c32b55226e2a93bf0f0bb7e0155581de8e525
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page