BetterErrors/better_errors
Better error page for Rack apps
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓7 active contributors
- ✓MIT licensed
- ✓CI configured
Show 3 more →Show less
- ✓Tests present
- ⚠Stale — last commit 2y ago
- ⚠Single-maintainer risk — top contributor 88% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/bettererrors/better_errors)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/bettererrors/better_errors on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: BetterErrors/better_errors
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/BetterErrors/better_errors shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 7 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Single-maintainer risk — top contributor 88% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live BetterErrors/better_errors
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/BetterErrors/better_errors.
What it runs against: a local clone of BetterErrors/better_errors — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in BetterErrors/better_errors | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 700 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of BetterErrors/better_errors. If you don't
# have one yet, run these first:
#
# git clone https://github.com/BetterErrors/better_errors.git
# cd better_errors
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of BetterErrors/better_errors and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "BetterErrors/better_errors(\\.git)?\\b" \\
&& ok "origin remote is BetterErrors/better_errors" \\
|| miss "origin remote is not BetterErrors/better_errors (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "lib/better_errors.rb" \\
&& ok "lib/better_errors.rb" \\
|| miss "missing critical file: lib/better_errors.rb"
test -f "lib/better_errors/middleware.rb" \\
&& ok "lib/better_errors/middleware.rb" \\
|| miss "missing critical file: lib/better_errors/middleware.rb"
test -f "lib/better_errors/error_page.rb" \\
&& ok "lib/better_errors/error_page.rb" \\
|| miss "missing critical file: lib/better_errors/error_page.rb"
test -f "lib/better_errors/stack_frame.rb" \\
&& ok "lib/better_errors/stack_frame.rb" \\
|| miss "missing critical file: lib/better_errors/stack_frame.rb"
test -f "lib/better_errors/repl.rb" \\
&& ok "lib/better_errors/repl.rb" \\
|| miss "missing critical file: lib/better_errors/repl.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 700 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~670d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/BetterErrors/better_errors"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Better Errors is a Rack middleware gem that replaces the default Rails/Rack error page with an interactive debugging interface. It provides full stack traces with syntax-highlighted source code inspection, live REPL access on any stack frame, local/instance variable inspection, and one-click editor links—transforming error pages from useless boilerplate into productive debugging tools. Simple gem structure: lib/better_errors/ contains core modules (error_page.rb for rendering, code_formatter/ for syntax highlighting in HTML/text variants, editor.rb for protocol handling). The middleware integrates as a Rack injection point. Static assets (SCSS, HTML templates) live in the public-facing layer, with feature screenshots documenting the UI.
👥Who it's for
Ruby/Rails developers in development environments who need to rapidly diagnose and fix application errors. They benefit from seeing code context, inspecting live application state via the REPL, and jumping directly to the error location in their editor instead of manually navigating files.
🌱Maturity & risk
Production-mature and actively maintained. The project has CI workflows (ci.yml, pull_request.yml, release.yml), extensive test coverage tracking via Coveralls, and supports Rails 4.2 through 6.1+ with multiple Pry versions (0.9–0.11) via gemfiles. Recent release automation (draft_release_update.yml) and Codacy integration indicate active stewardship.
Low risk for a mature development tool. The gem is narrowly scoped (error page rendering only, not production-critical), has no indication of abandoned status, and supports broad Ruby/Rails version ranges via appraisal-style gemfiles. Single-maintainer risk exists (no visible team in file structure), but the codebase is stable and the feature set complete.
Active areas of work
Active maintenance visible in GitHub Actions workflows: CI runs on pushes/PRs, draft releases are generated automatically, and multiple Gemfile combinations are tested (Rails 4.2–6.1, Pry 0.9–0.11, Haml variants). The project appears to be in steady-state optimization rather than feature development.
🚀Get running
Clone and install: git clone https://github.com/BetterErrors/better_errors && cd better_errors && bundle install. Verify the Ruby version in .ruby-version, then run tests: bundle exec rake. To use in development: add gem 'better_errors' and gem 'binding_of_caller' to your Gemfile's :development group.
Daily commands:
No traditional 'dev server' (this is middleware, not an app). To test locally: bundle exec rake runs the test suite. To see it in action, install in a Rails/Rack app in development and trigger an error. Set EDITOR env var to test editor links; set BETTER_ERRORS_INSIDE_FRAME=1 if running inside an iframe.
🗺️Map of the codebase
lib/better_errors.rb— Main entry point and public API surface; every contributor must understand the gem's initialization and core configuration.lib/better_errors/middleware.rb— Rack middleware that intercepts exceptions; the load-bearing integration point for all Rack/Rails apps.lib/better_errors/error_page.rb— Assembles the error page HTML/text output; orchestrates all error rendering logic.lib/better_errors/stack_frame.rb— Parses and represents individual stack frames with local variables and source code context.lib/better_errors/repl.rb— Base interface for the live shell/REPL functionality; coordinates between basic and Pry implementations.lib/better_errors/templates/main.erb— Primary HTML template for the error page; defines the visual structure and user interaction.lib/better_errors/code_formatter.rb— Syntax highlighting and code rendering abstraction; handles HTML and text formatting of source snippets.
🛠️How to make changes
Add a new REPL implementation (e.g., IRuby)
- Create a new file in lib/better_errors/repl/ implementing the REPL interface (
lib/better_errors/repl/iruby.rb) - Implement #setup(binding), #eval(code, binding), and #destroy methods following the Pry/Basic pattern (
lib/better_errors/repl/iruby.rb) - Register the new REPL in the factory method in lib/better_errors/repl.rb with a conditional check (
lib/better_errors/repl.rb) - Add tests following the shared_examples pattern in spec/better_errors/repl/shared_examples.rb (
spec/better_errors/repl/iruby_spec.rb)
Add a new editor integration
- Define the editor case and URL scheme in lib/better_errors/editor.rb in the #initialize method (
lib/better_errors/editor.rb) - Implement a #url_for_file method that returns the editor-specific open URL (
lib/better_errors/editor.rb) - Add test cases in spec/better_errors/editor_spec.rb validating the URL generation (
spec/better_errors/editor_spec.rb) - Document the new editor option in README.md under configuration (
README.md)
Add a new exception hint heuristic
- Add a new method in lib/better_errors/exception_hint.rb that detects the error pattern (
lib/better_errors/exception_hint.rb) - Call the new method from the #hint method and return a helpful String (
lib/better_errors/exception_hint.rb) - Add test cases in spec/better_errors/exception_hint_spec.rb covering the error scenario (
spec/better_errors/exception_hint_spec.rb) - Update lib/better_errors/templates/main.erb to display the hint if present (
lib/better_errors/templates/main.erb)
Enhance code syntax highlighting colors
- Edit lib/better_errors/style/syntax_highlighting.scss to adjust light theme token colors (
lib/better_errors/style/syntax_highlighting.scss) - Edit lib/better_errors/style/syntax_highlighting_dark.scss for dark theme colors (
lib/better_errors/style/syntax_highlighting_dark.scss) - Recompile main.scss and ensure the updated CSS is embedded via error_page_style.rb (
lib/better_errors/error_page_style.rb) - Test in both light and dark modes by triggering an exception in development (
lib/better_errors/templates/main.erb)
🔧Why these technologies
- Rack middleware — Provides transparent exception interception in any Rack app without modifying application code; works with Rails, Sinatra, and custom Rack stacks.
- ERB templates — Simple, server-side rendering of error pages with inline CSS/JS; no build step required for gem distribution.
- Pry gem (optional) — Powerful interactive debugging; graceful fallback to basic IRB if not installed.
- SCSS stylesheets — Variables and mixins enable light/dark theme variants; compiled CSS is inlined to avoid asset pipeline dependencies.
- Binding inspection (binding_of_caller) — Enables capturing local variables and evaluating code in the exception context; critical for interactive debugging.
⚖️Trade-offs already made
-
Inline all CSS/JS into HTML response (no separate assets)
- Why: Avoids dependency on asset pipeline; works in any Rack app. Error pages must be self-contained.
- Consequence: Larger HTML payload per error; no browser caching of static assets. Trade-off favors simplicity and portability.
-
Binding-based local variable inspection over static analysis
- Why: Provides accurate, runtime variable state. Static analysis cannot resolve dynamic Ruby values.
- Consequence: Requires binding_of_caller gem and MRI Ruby; does not work on JRuby or other implementations without that support.
-
Pry as optional dependency with graceful fallback
- Why: Pry is more powerful but adds size/boot time. Basic IRB always available.
- Consequence: Users without Pry get less interactive debugging; two code paths to maintain.
-
REPL runs in exception context (same binding)
- Why: Allows inspecting local variables and re-running code with same scope.
- Consequence: undefined
🪤Traps & gotchas
binding_of_caller requirement: Advanced features (REPL, variable inspection, pretty stack frame names) silently degrade if this optional gem is absent—check Gemfile installation. EDITOR env var: Editor links only work if $EDITOR is set; without it, links appear but don't open anything. config.consider_all_requests_local = true: Must be set in config/environments/development.rb (mentioned in README) or error page won't activate in Rails. BETTER_ERRORS_INSIDE_FRAME=1: Required only if behind CSP restrictions or iframe—otherwise unnecessary. Rack version drift: Test against your exact Rack version via appropriate gemfile (rack.gemfile vs. rails*.gemfile) to catch integration issues.
🏗️Architecture
💡Concepts to learn
- Rack Middleware — Better Errors is implemented as Rack middleware that intercepts exceptions and renders the error page; understanding middleware chains and env manipulation is core to how it works
- Syntax Highlighting / Tokenization — The code_formatter modules use syntax highlighting to make source code readable in the error page; understanding tokenization and HTML escaping prevents XSS and improves code readability
- REPL (Read-Eval-Print Loop) — Better Errors' live shell feature provides an interactive REPL (powered by Pry) at each stack frame; understanding REPL mechanics and binding contexts is essential for the variable inspection feature
- Runtime Binding & Frame Introspection — The binding_of_caller gem provides access to caller frames at runtime; understanding Ruby's Binding object and stack frame introspection is how local/instance variable inspection works
- Protocol Handlers / URL Schemes — Editor links in Better Errors use custom protocol handlers (txmt:// for TextMate, etc.) to open files; understanding how browsers dispatch to protocol handlers enables editor integration
- Content Security Policy (CSP) & Iframe Context — The BETTER_ERRORS_INSIDE_FRAME flag addresses CSP restrictions on protocol links; understanding CSP directives and same-origin policy is necessary for deployment in restricted contexts
- Stack Trace Parsing & Source Location Mapping — Better Errors parses Ruby stack traces to extract file names, line numbers, and method names, then maps them to source code; understanding backtrace format and source location APIs is foundational
🔗Related repos
rails/rails— Rails includes a default error page that Better Errors replaces; understanding Rails' ActionDispatch::ShowExceptions middleware clarifies the integration pointbanister/binding_of_caller— Optional but critical dependency for REPL and variable inspection features; provides the runtime binding access that enables advanced debuggingpry/pry— Underlying REPL engine used by Better Errors' live shell feature; tested against Pry 0.9–0.11 in gemfiles to maintain compatibilitythoughtbot/web-console— Competing Rails error page enhancement (now built into modern Rails); similar goals (interactive error debugging) but different approach (web-based console vs. error page REPL)rack/rack— Foundational dependency; Better Errors is pure Rack middleware, so understanding Rack's middleware chain and env hash is essential for integration
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive test coverage for lib/better_errors/repl/ implementations
The repo has two REPL implementations (basic.rb and pry.rb) but no dedicated test files visible in the structure. Given the complexity of REPL interactions and the multiple gemfile variations for different Pry versions (pry09.gemfile, pry010.gemfile, pry011.gemfile), there's significant risk of regressions. New contributors could add unit tests for both REPL classes, covering fallback behavior when Pry is unavailable, command execution, and exception handling.
- [ ] Create spec/better_errors/repl/basic_spec.rb with tests for BasicREPL initialization and command execution
- [ ] Create spec/better_errors/repl/pry_spec.rb with tests for PryREPL, including version compatibility checks
- [ ] Add integration tests verifying REPL behavior across different Pry versions using the existing gemfiles
- [ ] Ensure tests cover graceful fallback when Pry is not available
Add missing tests for lib/better_errors/stack_frame.rb edge cases
stack_frame.rb is a critical component for parsing and displaying error context, but there's no visible dedicated test file. This class handles source file parsing, line extraction, and frame filtering which are error-prone. Contributors should add comprehensive tests covering different file types, encoding issues, missing files, and line number boundary conditions.
- [ ] Create spec/better_errors/stack_frame_spec.rb with unit tests
- [ ] Add tests for file not found, permission denied, and encoding edge cases
- [ ] Test with files at line 1, last line, and beyond file boundaries
- [ ] Add tests for filtering frames (Rails internal, gems, etc.) as seen in middleware.rb
Add CI workflow for testing against multiple Ruby versions and Rails combinations
While .github/workflows/ci.yml exists, the gemfile matrix (rails42 through rails61, with multiple Pry versions) suggests the project supports broad version ranges. The current CI setup may not be testing all meaningful combinations. Adding a comprehensive matrix job would catch version-specific bugs early. This is evident from the 20+ gemfiles that need validation.
- [ ] Extend .github/workflows/ci.yml with a matrix strategy testing Ruby 2.6+ and Rails 4.2-6.1
- [ ] Add separate job that tests all gemfiles in gemfiles/ directory (or at least major ones: rails42, rails52, rails61)
- [ ] Configure the matrix to fail fast but also show which specific combination failed
- [ ] Document the testing matrix in README.md or CONTRIBUTING.md
🌿Good first issues
- Add test coverage for lib/better_errors/code_formatter/text.rb: the plaintext code formatting variant appears to lack dedicated test files (no spec/code_formatter_text_spec.rb visible), making it a low-risk place to add comprehensive unit tests.
- Document supported editors in lib/better_errors/editor.rb: the README mentions 'see the wiki' for editor setup, but the actual protocol mapping code in editor.rb is the source of truth—extract and inline editor configuration docs with examples for VS Code, Sublime, Vim, Emacs.
- Add integration test for BETTER_ERRORS_INSIDE_FRAME=1 behavior: the feature is mentioned in README but no visible test in the CI matrix validates that target=_blank links are correctly generated when the env var is set—write a scenario test.
⭐Top contributors
Click to expand
Top contributors
- @RobinDaugherty — 88 commits
- @haileys — 6 commits
- @peaonunes — 2 commits
- [@Jack Jennings](https://github.com/Jack Jennings) — 1 commits
- @wonda-tea-coffee — 1 commits
📝Recent commits
Click to expand
Recent commits
fde3b70— Add exception and comments to explain the issue (RobinDaugherty)8b1e2c9— Merge pull request #520 from jackjennings/master (RobinDaugherty)6f2d1ea— Require sassc only in development (Jack Jennings)55ce4f6— bump year (haileys)b5be43d— remove another coveralls step (haileys)5c83173— remove failing coveralls step (haileys)3bcbb4b— trigger release draft (haileys)75f5edf— update license details (haileys)b252041— update authorship (haileys)00447eb— Release Drafter (RobinDaugherty)
🔒Security observations
- High · Information Disclosure via Error Page in Production —
lib/better_errors/middleware.rb, lib/better_errors/error_page.rb. Better Errors is designed to display detailed error information including local variables, source code, and stack traces. If enabled in production, this middleware could expose sensitive information such as API keys, database credentials, user data, and application internals to attackers. Fix: Ensure Better Errors is only enabled in development environments. Implement strict environment checks and verify that the middleware is disabled in production via configuration gates (e.g., Rails.env.development?). - High · Potential XSS via Template Rendering —
lib/better_errors/templates/main.erb, lib/better_errors/templates/text.erb, lib/better_errors/templates/variable_info.erb. The error page uses ERB templates (lib/better_errors/templates/main.erb, text.erb, variable_info.erb) to render error details and variable inspection. If exception messages, variable values, or source code are not properly escaped, this could lead to XSS attacks when displaying user-controlled or untrusted input. Fix: Ensure all dynamic content in ERB templates is properly HTML-escaped. Use <%= ... %> with auto-escaping enabled (default in modern Rails) rather than <%== ... %>. Verify that exception messages and variable inspection outputs are sanitized before rendering. - High · Potential Code Injection via REPL Console —
lib/better_errors/repl/pry.rb, lib/better_errors/repl/basic.rb, lib/better_errors/middleware.rb. Better Errors provides an interactive REPL (Read-Eval-Print Loop) console accessible from the error page (lib/better_errors/repl/pry.rb, basic.rb). This allows arbitrary Ruby code execution in the application context. If the error page is accessible to unauthorized users, it could enable remote code execution. Fix: Implement strict access controls for the error page and REPL console. Disable REPL in production. Consider implementing IP whitelisting or authentication mechanisms. Ensure the middleware validates requests before allowing REPL access. - Medium · Missing Security Headers in Error Response —
lib/better_errors/middleware.rb. The error page middleware may not set appropriate security headers (Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, etc.) on error responses, potentially allowing clickjacking, MIME-type sniffing, or other browser-based attacks. Fix: Add security headers to all error page responses. Set X-Content-Type-Options: nosniff, X-Frame-Options: DENY, X-XSS-Protection: 1; mode=block, and a restrictive Content-Security-Policy header. - Medium · Insecure Deserialization Risk in Variable Inspection —
lib/better_errors/inspectable_value.rb. The lib/better_errors/inspectable_value.rb module performs variable inspection and serialization for display. Improper handling of complex Ruby objects or unsafe serialization methods could lead to security issues. Fix: Audit the variable inspection logic to ensure safe serialization practices. Avoid using eval() or unsafe deserialization. Implement strict object type checking and value length limits to prevent DoS. - Medium · Potential Path Traversal in Editor Integration —
lib/better_errors/editor.rb. The lib/better_errors/editor.rb module handles opening files in external editors. If the file path is not properly validated, an attacker could potentially trigger opening of arbitrary files on the system. Fix: Implement strict path validation and sanitization. Use absolute paths and verify that requested files are within the application directory. Reject any path containing '..' or symbolic links pointing outside the app. - Low · Missing Dependency Pinning in Development —
gemfiles/ directory, better_errors.gemspec. Multiple gemfiles (gemfiles/ directory) have different versions of dependencies (Pry 0.9, 0.10, 0.11, Rails 4.2-6.1). Without explicit version constraints or lock files in CI, supply chain attacks via compromised intermediate versions are possible. Fix: Use Gemfile.lock in CI/CD pipelines. Pin exact versions of critical dependencies. Regularly audit and update dependencies through a controlled process.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.