SentryBugFixer watches your Sentry issues, and for any one you pick (or via webhook) it clones the GitLab repo, fixes the bug with an LLM agent, then commits, pushes, and opens a merge request — all from a small self-hostable dashboard.
Sentry issue / webhook → clone the GitLab repo → LLM agent fixes it → branch + commit + push → open MR
The agent is a minimal LLM + bash loop inspired by
mini-swe-agent: a linear message history
where every action is a shell command run in the cloned repo.
- 🖥️ Dashboard — register projects (GitLab + Sentry URLs), list unresolved issues, fix any with one click.
- 🔌 Sentry webhook — auto-fix issues as they arrive.
- 📡 Live logs — stream each fix step over WebSocket in a popup.
- ✍️ Custom instructions — add guidance to the agent before a run.
- ♻️ Resumable — interrupted/errored fixes continue from the last step instead of re-paying for it.
- 🧮 Cost tracking — per-fix token usage and USD cost, with a model price table and per-issue history.
- 🔁 MR lifecycle — issues show Under review while the MR is open, and are resolved when it closes/merges.
- 🐳 Production-ready — multi-arch Docker image (amd64 + arm64), health check, non-root, nginx sample, CI.
- An Anthropic API key (
ANTHROPIC_API_KEY) - A Sentry auth token (
project:read,event:read) - A GitLab token (
api,write_repository) - For local runs: Python 3.12+ and git on
PATH(the Docker image bundles git).
Copy .env.example to .env and fill these in — both run modes read it.
git clone https://github.com/m4ary/SentryBugFixer.git
cd SentryBugFixer
cp .env.example .env # fill in your tokens
docker compose up -d # pulls ghcr.io/m4ary/sentrybugfixer:latestOpen http://localhost:8000 · logs: docker compose logs -f · stop: docker compose down.
The image is multi-arch (works on x86-64 Ubuntu servers and ARM/Apple Silicon), runs as a
non-root user, exposes /health, and persists the SQLite db, cloned repos, and resume
transcripts to the sbf-data volume — back it up to keep history.
Pin a version for production: set
image: ghcr.io/m4ary/sentrybugfixer:1.0.0indocker-compose.yml. To build from source instead, uncommentbuild: .there and rundocker compose up -d --build.
Plain Docker, without compose:
docker run -d --name sentrybugfixer -p 8000:8000 \
--env-file .env -e SBF_DATA_DIR=/data -v sbf-data:/data \
ghcr.io/m4ary/sentrybugfixer:latestgit clone https://github.com/m4ary/SentryBugFixer.git
cd SentryBugFixer
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # fill in your tokens
python -m sentrybugfixer # single process (production-style)
# or, for development with auto-reload:
./dev.shOpen http://127.0.0.1:8000.
- Add a project — name, GitLab repo URL, Sentry project URL, default branch.
- View issues — lists unresolved Sentry issues for that project.
- Fix it — optionally add instructions, then go. It clones the repo, runs the agent,
pushes
issue/<id>, and opens a merge request. Watch progress live; the MR link appears when it's done, and the issue flips to Under review.
Point a Sentry Internal Integration (or alert webhook) at:
POST https://<your-host>/webhook/sentry
SentryBugFixer matches the webhook's project slug to a configured project and starts a fix
automatically. Set SENTRY_WEBHOOK_SECRET to reject unsigned calls (checked against the
sentry-hook-signature header).
All settings come from the environment / .env (see .env.example):
| Variable | Default | Purpose |
|---|---|---|
ANTHROPIC_API_KEY |
— | The bug-fixing agent (required) |
SBF_MODEL |
claude-opus-4-8 |
Agent model |
SBF_EFFORT |
high |
Thinking/effort: low…max |
SENTRY_AUTH_TOKEN |
— | Read issues (required) |
SENTRY_HOST |
https://sentry.io |
For self-hosted Sentry |
SENTRY_WEBHOOK_SECRET |
— | Shared secret for the webhook |
GITLAB_TOKEN |
— | Clone + open MRs (required) |
GITLAB_HOST |
https://gitlab.com |
For self-hosted GitLab |
SBF_MAX_STEPS |
40 |
Max agent steps per fix |
SBF_BASH_TIMEOUT |
180 |
Per-command timeout (seconds) |
SBF_HOST / SBF_PORT |
127.0.0.1 / 8000 |
Server bind address |
SBF_DATA_DIR |
./data |
SQLite db, clones, transcripts |
| File | Responsibility |
|---|---|
sentrybugfixer/agent.py |
Minimal Anthropic agent loop with a single bash tool (streaming, resumable) |
sentrybugfixer/sentry_client.py |
List issues, fetch issue + stack trace, parse webhooks, resolve issues |
sentrybugfixer/gitlab_client.py |
Clone, branch/commit/push, open & inspect merge requests |
sentrybugfixer/fixer.py |
Orchestrates one fix end-to-end (background thread) |
sentrybugfixer/pricing.py |
Model price table + cost computation |
sentrybugfixer/db.py |
SQLite storage for projects and jobs (auto-migrating) |
sentrybugfixer/events.py |
Thread → WebSocket pub/sub for live logs |
sentrybugfixer/app.py |
FastAPI: dashboard API, webhook, health, WebSocket |
sentrybugfixer/static/ |
The dashboard (vanilla HTML/CSS/JS) |
- Run a single instance. Fix jobs run in background threads and stream over an
in-memory broker, and storage is SQLite — do not run multiple uvicorn workers or
replicas; that splits job state and the live log stream. One container handles many fixes.
To scale further, move the broker to Redis and the db to Postgres (the
Databaseclass is isolated for this). - Put it behind HTTPS (nginx/Caddy/Traefik). A sample with the required
/ws/WebSocket upgrade headers is indeploy/nginx.conf. - Secrets come from the environment — never baked into the image (
.envis in.dockerignore). Use your platform's secret manager in production. - CI (
.github/workflows/docker.yml) builds and pushes the multi-arch image to GHCR on pushes tomainandv*tags.
- Git operations (branch, commit, push, MR) are done deterministically in Python — the
agent only edits files, it never pushes. Commits are unsigned (
commit.gpgsign=false) so they work in headless/containerized environments. - Branch name is
issue/<sentry-issue-id>; pushes use--force-with-lease. - Each fix runs in its own cloned checkout under
SBF_DATA_DIR/repos/. - Starting a fix for an issue already queued/running returns the existing job (no duplicates).
MIT © Mshary