ai-error — AI Self-Healing Error Handling for Python
ai-error (ai_error) is a decorator that makes Python code repair itself. Decorate a function with @ai_error. When it raises, an LLM (OpenAI, Anthropic Claude, Google Gemini, Groq, Mistral, Ollama, or any OpenAI-compatible endpoint) rewrites only the failing function, the module is reloaded, and the call is retried. If the fix belongs outside that function, you get an AIHealError that explains exactly what to change, with secrets redacted.
Keywords: self-healing Python, AI auto-fix, LLM exception handling, AI debugging, automatic bug fixing, OpenAI Python error handling, Claude Python error handling, Gemini Python error recovery, PyPI self-healing library.
Install
pip install ai-error
Requires Python 3.9+. There are no runtime dependencies. It uses only the standard library.
Quick start
from ai_error import ai_error
@ai_error # set OPENAI_API_KEY (or ANTHROPIC_API_KEY, GEMINI_API_KEY, ...) first
def parse_price(order):
return float(order["amount"].replace("$", "")) * 1.2 # KeyError if "amount" is missing
parse_price({"amount": "$10"}) # 12.0
parse_price({"total": "$10"}) # repaired in place, reloaded, retried
Works with plain functions, async def functions, and in @ai_error(provider="anthropic", max_attempts=2) form.
How the self-healing loop works
- Your function runs normally.
- On exception, the traceback is searched for the innermost frame inside your project. The AST (Python's
astmodule) isolates the smallest function containing that line. - The function source, the exception, and the last traceback lines go to the configured LLM. Secrets are redacted first.
- The model returns a replacement for that function. ai-error checks that it is one valid function and that the whole file still parses.
- The original file is backed up to
.ai-error/backups/, then the patch is written. - The module is reloaded with
importlib.reload, and the call is retried. This repeats up tomax_attempts. - If the model says the fix is elsewhere, or a step fails,
AIHealErroris raised with plain-language advice and no secrets.
Providers
| Provider | Environment variable | Usage |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
@ai_error(provider="openai") |
| Anthropic (Claude) | ANTHROPIC_API_KEY |
@ai_error(provider="anthropic") |
| Google Gemini | GEMINI_API_KEY / GOOGLE_API_KEY |
@ai_error(provider="gemini") |
| Groq | GROQ_API_KEY |
@ai_error(provider="groq") |
| Mistral | MISTRAL_API_KEY |
@ai_error(provider="mistral") |
| DeepSeek | DEEPSEEK_API_KEY |
@ai_error(provider="deepseek") |
| xAI (Grok) | XAI_API_KEY |
@ai_error(provider="xai") |
| OpenRouter | OPENROUTER_API_KEY |
@ai_error(provider="openrouter", model="anthropic/claude-3.5-haiku") |
| Together AI | TOGETHER_API_KEY |
@ai_error(provider="together") |
| Fireworks AI | FIREWORKS_API_KEY |
@ai_error(provider="fireworks") |
| Perplexity | PERPLEXITY_API_KEY |
@ai_error(provider="perplexity") |
| Cerebras | CEREBRAS_API_KEY |
@ai_error(provider="cerebras") |
| Ollama (local) | none | @ai_error(provider="ollama") |
| Any OpenAI-compatible API | yours | @ai_error(provider="custom", base_url="https://my-host/v1", model="my-model", api_key="...") |
| Any LLM (full control) | yours | @ai_error(complete=lambda system, prompt: "...") |
You can pass api_key="..." directly for any provider instead of (or alongside) an env var:
@ai_error(provider="groq", api_key="gsk_...", model="llama-3.3-70b-versatile")
def parse_price(order): ...
Autodetection follows the environment keys. Override the model with model= or AI_ERROR_MODEL. Choose the provider with AI_ERROR_PROVIDER.
Options
| Option | Default | Purpose |
|---|---|---|
name |
function name | Attribute name used to reload the patched function |
max_attempts |
3 |
Fix-and-retry rounds per call |
max_fixes |
10 |
Total automatic fixes per decorated function |
max_lines |
200 |
Refuse to edit longer functions |
allowed_roots |
[os.getcwd()] |
Only files under these folders can change |
dry_run |
False |
Save proposals to .ai-error/proposals/ instead of applying |
should_heal |
None |
Predicate that decides which exceptions are eligible |
on_fix |
None |
Callback after each applied fix |
Set AI_ERROR_DISABLE=1 to turn it off everywhere, or AI_ERROR_DRY_RUN=1 to force dry runs.
Safety model
- Scope: only the innermost failing function is edited. Files outside
allowed_roots, and anything insite-packages, are never touched. - Validation: patches must be one syntactically valid function, and the whole file must parse afterwards.
- Backups and rollback: every change is backed up first, and a patch that breaks module import is reverted automatically.
- Redaction: environment secrets, key-shaped strings, and absolute paths are removed before anything leaves your machine.
- No arguments sent: function arguments are never sent to the model.
- Budgets:
max_attemptsandmax_fixescap the loop.
Use dry_run=True while evaluating, and review .ai-error/backups/ in version control. Add .ai-error/ to .gitignore. Do not enable this unattended in production without review.
Limitations
- Patches apply to module-level functions (or functions reachable by
namein their module). Methods and closures cause a restart request. - Other modules holding references to the old function keep using it. The decorated wrapper always calls the fixed version.
- Reloading re-runs the module's top-level code.
asyncfunctions run the healing step synchronously, which briefly blocks the event loop.- The entry script (
__main__) is never reloaded.
Error codes
AIHealError.code: SCOPE, UNFIXABLE, REJECTED, LLM, PROVIDER, BUDGET, LOAD, DRY_RUN, RESTART, EXHAUSTED. The original exception is chained as __cause__ for local debugging.
FAQ
Is this a replacement for try/except? It is a recovery layer. Unfixable errors still reach your code as AIHealError.
Does it cost money? Only when an exception occurs. Each attempt is one request containing one function.
Are fixes deterministic? No. Scope, validation, and backups are there because model output varies.
Development
git clone https://github.com/YOUR_USER/ai-error.git
cd ai-error/packages/python
pip install -e .
python -m unittest discover -s tests -v
License
MIT
Metadata
Release files for ai-error 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ai_error-0.2.0.tar.gz | 10.9 kB | Without provenance | Details for ai_error-0.2.0.tar.gz |
Release files / ai_error-0.2.0.tar.gz
| Download URL | ai_error-0.2.0.tar.gz |
|---|---|
| Size | 10.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3a4e211851b0b4cc0033c8d669774b13dc767a55ed3f6f4d6a5d2c1ec1b6aaa0
|
|
BLAKE2b-256 checksum How to use checksums |
f6fdc6e9fba13592136ae039341d209f0c1cd8d9149c5ab46142b0cad644b99e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Security for ai-error 0.2.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.