Skip to main content

butwhy

Stop guessing. Start understanding.

Your Python errors, explained — in plain English.

pip install butwhy → import butwhy → done.

PyPI Python 3.9+ License: MIT CI Zero Dependencies


The 30-second pitch

You know how Python tracebacks look like this:

Traceback (most recent call last):
  File "demo.py", line 2, in <module>
    result = data.split(",")
AttributeError: 'NoneType' object has no attribute 'split'

And then you spend 5 minutes on Google figuring out what went wrong?

why turns that into this:

============================================================
  AttributeError: 'NoneType' object has no attribute 'split'
  at demo.py:2
============================================================

  [pattern match · 93%]

  Summary:
    Calling .split() on None

  Cause:
    The variable data is None, not a string.
    This usually means a function returned None (maybe it
    forgot a return statement), or a lookup failed silently.

  Variables at error:
    data = None  (NoneType)

  Fix:
    Add a None check: if data is not None: data.split(",")
    Or trace back to find why data is None.

============================================================

One import. Zero config. Instant understanding.


Quick start

pip install butwhy
import butwhy  # That's it. Errors are now explained.

data = None
result = data.split(",")  # Boom — why explains it

Want AI-powered deep explanations?

Set one environment variable and why upgrades automatically:

export OPENAI_API_KEY=sk-...   # or ANTHROPIC_API_KEY

No API key? why still works — it falls back to built-in pattern matching that covers 12+ common error types with zero dependencies.

Prefer a local model? (No API key, fully offline)

# Install Ollama: https://ollama.com
ollama pull qwen2.5-coder:7b

export BUTWHY_PROVIDER=ollama

Three ways to use why

1. Global (recommended) — import butwhy

import butwhy

# Every uncaught exception in your script is now explained
1 / 0

2. Context manager — with butwhy.trace()

import butwhy

with butwhy.trace():
    # Only errors inside this block are explained
    risky_operation()

3. Decorator — @butwhy.explain

import butwhy

@butwhy.explain
def divide(a, b):
    return a / b

divide(10, 0)  # Error is explained, then re-raised

butwhy.fix() — Don't just explain, fix

After an error, call butwhy.fix() to get an AI-generated patch:

import butwhy

data = None
result = data.split(",")  # crashes

# In interactive mode:
>>> butwhy.fix()

  Suggested fix for demo.py:2
  --------------------------------------------------
  - result = data.split(",")
  + if data is not None:
  +     result = data.split(",")
  + else:
  +     result = []
  --------------------------------------------------

  Apply this fix? [y/N] y
  Applied fix to demo.py:2

Requires an AI provider (OpenAI/Anthropic/Ollama).


butwhy.this — The Zen of Why

>>> import butwhy
>>> butwhy.this
The Zen of Why, by why

Errors are not failures, they are teachers.
The traceback tells you what; why tells you why.
A good message answers the question before you ask it.
Read the variables, not just the line numbers.
...

Jupyter / IPython support

# In a notebook:
%load_ext why

# Or just:
import butwhy

# Now all cell errors are explained inline

Configuration

All settings via environment variables — no config file needed:

Variable Default Description
OPENAI_API_KEY — OpenAI API key (enables AI explanations)
ANTHROPIC_API_KEY — Anthropic API key (enables AI explanations)
BUTWHY_PROVIDER auto auto / openai / anthropic / ollama / patterns
BUTWHY_LANGUAGE en en / zh (Chinese)
BUTWHY_MODEL — Override the model name for the active provider
BUTWHY_TIMEOUT 30 API timeout in seconds
OLLAMA_URL http://localhost:11434 Ollama server URL
OLLAMA_MODEL qwen2.5-coder:7b Ollama model to use
BUTWHY_AUTOSTART 1 Set to 0 to disable auto-install of the hook
BUTWHY_NO_COLOR — Set to disable colored output
NO_COLOR — Standard no-color env var (respected)

Programmatic config

import butwhy

butwhy.set_config(butwhy.WhyConfig(
    provider="openai",
    openai_api_key="sk-...",
    language="zh",        # Chinese explanations
    show_source=True,
    show_vars=True,
    cache=True,
))

How it works

why uses a three-layer fallback to ensure you always get a useful explanation:

Layer When How Quality
AI explanation API key available Sends error + source context + variables to LLM Best — covers everything, gives specific fixes
Pattern matching No API key (or AI failed) Heuristic matching on 12+ common error types Good — covers the errors you hit daily
Enhanced traceback No pattern matched Colored output with variables and source context Fallback — still better than default

Key design principles:

  • Zero dependencies — pure Python stdlib. No rich, no httpx, no openai SDK. Just urllib and friends.
  • Never crashes — if the AI fails, pattern matching takes over. If that fails, you still get a prettier traceback.
  • Privacy-aware — your code only goes to an LLM if you explicitly set an API key. Pattern matching is 100% local.
  • Caching — repeated errors don't re-call the API. Cached for 7 days.

Comparison

Feature why rich better-exceptions stackprinter Copilot
Works without API key ✅ ✅ ✅ ✅ ❌
AI-powered explanations ✅ ❌ ❌ ❌ ✅
import and done ✅ ❌ ❌ ❌ ❌
Local model support ✅ Ollama — — — ❌
Zero dependencies ✅ ❌ ❌ ❌ —
Variable values in output ✅ ✅ ✅ ✅ ❌
Auto-fix (butwhy.fix()) ✅ ❌ ❌ ❌ ✅
Works in CI/CD & servers ✅ ✅ ✅ ✅ ❌
Chinese explanations ✅ ❌ ❌ ❌ ✅
Jupyter magic ✅ ❌ ❌ ❌ ❌

Supported error types (pattern matching)

The built-in pattern matcher covers these error types without any API key:

  • NameError — undefined variable
  • TypeError — wrong type operations
  • ValueError — invalid values (int conversion, unpacking)
  • IndexError — list index out of range
  • KeyError — missing dictionary key
  • AttributeError — wrong attribute/method (including NoneType)
  • ZeroDivisionError — division by zero
  • ImportError / ModuleNotFoundError — missing modules (with install hints)
  • FileNotFoundError — file path issues
  • SyntaxError — common syntax mistakes
  • RecursionError — infinite recursion
  • UnicodeDecodeError — encoding issues
  • IndentationError / TabError — indentation problems

More patterns are added regularly. With an AI provider, all error types are covered.


Examples

See the examples/ directory for runnable demos:

  • basic.py — common errors with pattern matching
  • with_ai.py — AI-powered deep explanations
  • jupyter_demo.ipynb — notebook usage
  • fix_demo.py — auto-fix workflow

Contributing

Contributions welcome! Especially:

  • New pattern matchers for error types not yet covered
  • Translations for BUTWHY_LANGUAGE
  • Bug reports and edge cases
git clone https://github.com/yourname/butwhy.git
cd butwhy
pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.


import butwhy — because every error deserves an explanation.

Metadata

Release files for butwhy 0.1.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 butwhy 0.1.0
File Size Uploaded
butwhy-0.1.0.tar.gz 32.7 kB Details

Built distribution (wheel)

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

Total release size: 63.9 kB

Release files / butwhy-0.1.0.tar.gz

Download URL butwhy-0.1.0.tar.gz
Size 32.7 kB
Tags Source
SHA-256 checksum
How to use checksums
601f69ff33898557cc55c4d9cde693cd8a7b155d0b54610f4986049b5a065bab
BLAKE2b-256 checksum
How to use checksums
1760df5d4a09bf4f8a195f83c8305989a749a226272e89f57bcc243b30810b10
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release files / butwhy-0.1.0-py3-none-any.whl

Download URL butwhy-0.1.0-py3-none-any.whl
Size 31.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
635f50318b7746f34bd4a2c625712abd61db7b19cfc46193a38166373e038060
BLAKE2b-256 checksum
How to use checksums
4a43b8510f4d6726501513c1b8d4df8935cee8ad2076422cc60292e66ea6fec2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.1.0 This release

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