Rails Debugging Guide
This skill provides a systematic approach to debugging Ruby on Rails applications, covering common error patterns, modern debugging tools, and proven troubleshooting techniques.
Common Error Patterns
- ActiveRecord::RecordNotFound
Symptoms: Rails fails to find a record in the database when using find or find_by! methods.
Diagnosis:
Check if record exists
rails console
Model.exists?(id) Model.where(conditions).count
Inspect the query being generated
Model.where(conditions).to_sql
Common Causes:
-
Record was deleted but reference still exists
-
Incorrect ID passed from params
-
Scopes filtering out the record
-
Multi-tenancy issues (wrong tenant context)
Solutions:
Use find_by instead of find (returns nil instead of raising)
Model.find_by(id: params[:id])
Handle gracefully in controller
def show @record = Model.find_by(id: params[:id]) render_not_found unless @record end
Or rescue the exception
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
- ActionController::RoutingError
Symptoms: 404 error - requested URL doesn't exist in the application.
Diagnosis:
List all routes
rails routes
Search for specific route
rails routes | grep resource_name
Check route with specific path
rails routes -g path_pattern
Common Causes:
-
Missing route definition in config/routes.rb
-
Typo in URL or route helper
-
Wrong HTTP verb
-
Namespace/scope mismatch
-
Engine routes not mounted
Solutions:
Verify route exists
Rails.application.routes.recognize_path('/your/path', method: :get)
Add missing route
resources :users, only: [:show, :index]
Check for namespace issues
namespace :api do resources :users end
- NoMethodError (undefined method for nil:NilClass)
Symptoms: Calling a method on nil object.
Diagnosis:
Add debugging breakpoint
binding.break # Ruby 3.1+ / Rails 7+ debugger # Alternative byebug # Legacy
Check object state
puts object.inspect Rails.logger.debug object.inspect
Common Causes:
-
Database query returned no results
-
Association not loaded
-
Hash key doesn't exist
-
Typo in variable/method name
Solutions:
Safe navigation operator
user&.profile&.name
Null object pattern
user.profile || NullProfile.new
Use presence
params[:key].presence || default_value
Guard clauses
return unless user.present?
- N+1 Query Problems
Symptoms: Slow page loads, excessive database queries in logs.
Diagnosis:
Check logs for repeated queries
tail -f log/development.log | grep SELECT
Use Bullet gem for automatic detection
Gemfile
gem 'bullet', group: :development
config/environments/development.rb
config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.rails_logger = true end
Common Causes:
-
Iterating over collection and accessing associations
-
Missing includes or preload
-
Calling methods that trigger queries in views
Solutions:
Eager loading with includes
User.includes(:posts, :comments).all
Preload for large datasets
User.preload(:posts).find_each do |user| user.posts.each { |post| ... } end
Use joins for filtering
User.joins(:posts).where(posts: { published: true })
Counter cache for counts
belongs_to :user, counter_cache: true
- Database Migration Failures
Symptoms: Migrations fail, schema out of sync, rollback errors.
Diagnosis:
Check migration status
rails db:migrate:status
View pending migrations
rails db:abort_if_pending_migrations
Check current schema version
rails runner "puts ActiveRecord::Migrator.current_version"
Common Causes:
-
Column/table already exists
-
Foreign key constraint violations
-
Data type incompatibility
-
Missing index
-
Irreversible migration
Solutions:
Rollback and retry
rails db:rollback rails db:migrate
Reset database (development only!)
rails db:drop db:create db:migrate
Fix stuck migration
rails runner "ActiveRecord::SchemaMigration.where(version: 'XXXXXX').delete_all"
Use Strong Migrations gem for safety
gem 'strong_migrations'
- ActionController::InvalidAuthenticityToken
Symptoms: CSRF token mismatch on POST/PUT/PATCH/DELETE requests.
Diagnosis:
Check if token is present in form
<%= csrf_meta_tags %>
Verify token in request headers
request.headers['X-CSRF-Token']
Common Causes:
-
Missing CSRF meta tags in layout
-
JavaScript not sending token with AJAX
-
Session expired
-
Caching issues with forms
Solutions:
<!-- Add to layout head --> <%= csrf_meta_tags %>
<!-- JavaScript AJAX setup --> <script> $.ajaxSetup({ headers: { 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content } }); </script>
For API endpoints, skip verification
skip_before_action :verify_authenticity_token, only: [:api_action]
Or use null session
protect_from_forgery with: :null_session
- ActionView::Template::Error
Symptoms: View rendering fails with template errors.
Diagnosis:
Error message shows file and line number
Check the specific template mentioned
Debug variables in view
<%= debug @variable %> <%= @variable.inspect %>
Use better_errors gem for interactive debugging
gem 'better_errors', group: :development gem 'binding_of_caller', group: :development
Common Causes:
-
Undefined variable in view
-
Missing partial
-
Syntax error in ERB
-
Helper method not defined
-
Nil object method call
Solutions:
<!-- Check for nil before rendering --> <% if @user.present? %> <%= @user.name %> <% end %>
<!-- Use try for potentially nil objects --> <%= @user.try(:name) %>
<!-- Safe navigation --> <%= @user&.name %>
- Asset Pipeline Issues
Symptoms: CSS/JS not loading, missing assets in production, Sprockets errors.
Diagnosis:
Check asset paths
rails assets:precompile --trace
View compiled assets
ls -la public/assets/
Check manifest
cat public/assets/.sprockets-manifest*.json
Common Causes:
-
Asset not in load path
-
Missing precompile directive
-
Incorrect asset helper usage
-
Webpacker/esbuild configuration issues
Solutions:
Add to precompile list
config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( custom.js custom.css )
Use correct helpers
<%= javascript_include_tag 'application' %> <%= stylesheet_link_tag 'application' %> <%= image_tag 'logo.png' %>
For Rails 7+ with import maps
<%= javascript_importmap_tags %>
- Gem Conflicts and Bundler Issues
Symptoms: Bundle install fails, version conflicts, LoadError.
Diagnosis:
Check gem versions
bundle info gem_name
View dependency tree
bundle viz
Check for outdated gems
bundle outdated
Verify Bundler version
bundle --version
Common Causes:
-
Incompatible gem versions
-
Platform-specific gems
-
Missing native extensions
-
Lockfile out of sync
Solutions:
Update specific gem
bundle update gem_name
Clean reinstall
rm Gemfile.lock bundle install
Use specific version
gem 'problematic_gem', '~> 1.0'
Platform constraints
gem 'specific_gem', platforms: :ruby
- NameError (Uninitialized Constant)
Symptoms: Ruby can't find class, module, or constant.
Diagnosis:
Check if constant is defined
defined?(MyClass)
View autoload paths
puts ActiveSupport::Dependencies.autoload_paths
Check zeitwerk loader
Rails.autoloaders.main.dirs
Common Causes:
-
File not in autoload path
-
Typo in class/module name
-
Wrong file naming convention
-
Circular dependency
-
Missing require statement
Solutions:
Ensure file naming matches class name
app/models/user_profile.rb -> class UserProfile
Add to autoload paths if needed
config/application.rb
config.autoload_paths << Rails.root.join('lib')
Explicit require for lib files
require_relative '../lib/my_library'
Debugging Tools
Built-in Debug Gem (Rails 7+ / Ruby 3.1+)
The modern default debugger for Rails applications.
Add breakpoint
debugger binding.break binding.b
Configuration
Gemfile
gem 'debug', group: [:development, :test]
Disable in CI
Set RUBY_DEBUG_ENABLE=0
Common Commands:
Navigation
n / next - Step over s / step - Step into c / continue - Continue execution q / quit - Exit debugger
Inspection
p / pp - Print/pretty print info - Show local variables bt / backtrace - Show call stack
Breakpoints
break file.rb:10 - Set breakpoint break Class#method - Break on method delete 1 - Delete breakpoint
Byebug (Legacy Projects)
Gemfile
gem 'byebug', group: [:development, :test]
Add breakpoint
byebug
Commands similar to debug gem
next, step, continue, quit display @variable where # backtrace
Rails Console
Start console
rails console rails c
Sandbox mode (rollback all changes)
rails console --sandbox
Specific environment
RAILS_ENV=production rails console
Useful Console Techniques:
Reload code changes
reload!
Run SQL directly
ActiveRecord::Base.connection.execute("SELECT 1")
Time queries
Benchmark.measure { Model.all.to_a }
Find slow queries
ActiveRecord::Base.logger = Logger.new(STDOUT)
Test helpers
app.get '/path' app.response.body
Better Errors Gem
Gemfile
group :development do gem 'better_errors' gem 'binding_of_caller' end
Provides interactive error pages with:
-
Full stack trace with source code
-
Interactive REPL at each frame
-
Variable inspection
-
Local variable values
Bullet Gem (N+1 Detection)
Gemfile
gem 'bullet', group: :development
config/environments/development.rb
config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.bullet_logger = true Bullet.console = true Bullet.rails_logger = true Bullet.add_footer = true end
Rails Panel (Chrome Extension)
Provides browser-based debugging:
-
Request/response details
-
Database queries with timing
-
Rendered views
-
Log messages
-
Route information
Pry and Pry-Rails
Gemfile
gem 'pry-rails', group: [:development, :test]
Add breakpoint
binding.pry
Pry commands
ls # List methods/variables cd object # Change context show-source method # View source
The Four Phases of Rails Debugging
Phase 1: Reproduce and Isolate
Goal: Understand exactly what triggers the error.
Check recent changes
git diff HEAD~5 git log --oneline -10
Reproduce in console
rails console
Try to trigger the error
Check logs
tail -f log/development.log
Questions to Answer:
-
Can you reproduce it consistently?
-
What changed recently?
-
Does it happen in all environments?
-
What are the exact steps to trigger it?
Phase 2: Gather Information
Goal: Collect all relevant data about the error.
Add logging
Rails.logger.debug "Variable state: #{@variable.inspect}" Rails.logger.tagged("DEBUG") { Rails.logger.info "Custom message" }
Check stack trace
raise "Debug checkpoint"
Inspect request/response
request.inspect response.status params.inspect session.inspect
Data to Collect:
-
Full stack trace
-
Request parameters
-
Session state
-
Database state
-
Environment variables
-
Gem versions
Phase 3: Form and Test Hypothesis
Goal: Identify the root cause.
Add strategic breakpoints
debugger
Test assumptions
Model.find(id) rescue "Not found"
Check database state
rails dbconsole SELECT * FROM table WHERE condition;
Verify configuration
Rails.configuration.inspect ENV['KEY']
Common Hypotheses:
-
Data issue (missing/corrupt records)
-
Code logic error
-
Configuration mismatch
-
Race condition
-
External service failure
Phase 4: Fix and Verify
Goal: Implement fix and prevent regression.
Write failing test first
test "should handle missing record" do assert_raises(ActiveRecord::RecordNotFound) do get :show, params: { id: 0 } end end
Apply fix
Run tests
rails test test/path/to_test.rb
Verify in development
rails server
Test the scenario manually
Verification Checklist:
-
Original error no longer occurs
-
Test covers the fix
-
No new errors introduced
-
Works in all environments
Quick Reference Commands
Routes
All routes
rails routes
Filtered routes
rails routes -c users rails routes -g api rails routes | grep pattern
Specific route
rails routes -E # Expanded format
Database
Migration status
rails db:migrate:status
Run migrations
rails db:migrate rails db:migrate VERSION=XXXXXX
Rollback
rails db:rollback rails db:rollback STEP=3
Reset (drops, creates, migrates)
rails db:reset
Seed data
rails db:seed
Direct SQL access
rails dbconsole
Rails Runner
Execute Ruby code
rails runner "puts User.count" rails runner "User.find(1).update(active: true)" rails runner path/to/script.rb
With environment
RAILS_ENV=production rails runner "puts User.count"
Generators and Tasks
List all tasks
rails -T
List generators
rails generate --help
Task info
rails -D task_name
Cache
Clear cache
rails tmp:cache:clear
Clear all tmp
rails tmp:clear
In console
Rails.cache.clear
Logs and Debugging
Tail logs
tail -f log/development.log
Clear logs
rails log:clear
With grep filtering
tail -f log/development.log | grep -E "(ERROR|WARN)"
Testing
Run all tests
rails test
Specific file
rails test test/models/user_test.rb
Specific test
rails test test/models/user_test.rb:42
With verbose output
rails test -v
RSpec (if using)
bundle exec rspec bundle exec rspec spec/models/user_spec.rb
Console Tricks
Reload after code changes
reload!
Suppress output
User.all; nil
Time execution
Benchmark.measure { Model.expensive_query }
SQL logging
ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Base.logger = nil # Disable
Trace method calls
require 'tracer' Tracer.on { Model.method_call }
Find where method is defined
User.instance_method(:method_name).source_location User.method(:class_method).source_location
Environment-Specific Debugging
Development
config/environments/development.rb
config.consider_all_requests_local = true config.action_controller.perform_caching = false config.log_level = :debug
Production
Enable detailed errors temporarily (DANGEROUS)
RAILS_ENV=production rails console
Rails.application.config.consider_all_requests_local = true
Check production logs
heroku logs --tail
or
ssh server 'tail -f /app/log/production.log'
Safe debugging with exception tracking
Use Sentry, Rollbar, Honeybadger, etc.
Test
Verbose test output
rails test -v
Debug test database
rails dbconsole -e test
Keep test database
RAILS_ENV=test rails db:migrate
Security Considerations
When debugging, be careful about:
Never log sensitive data
Rails.logger.info params.except(:password, :credit_card)
Filter parameters
config/initializers/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [ :password, :password_confirmation, :credit_card, :cvv, :ssn, :secret, :token, :api_key ]
Don't expose stack traces in production
config.consider_all_requests_local = false
Use exception tracking services instead
Sentry, Rollbar, Honeybadger, Bugsnag
Additional Resources
-
Ruby on Rails Guides - Debugging
-
Ruby Debugging Tips 2025
-
Better Stack - Common Ruby Errors
-
Rollbar - Top 10 Rails Errors