| name | debug:rails |
| description | Debug Rails issues systematically. Use when encountering ActiveRecord errors like RecordNotFound, routing issues, N+1 query problems detected by Bullet, asset pipeline issues, migration failures, gem conflicts, ActionController errors, CSRF token problems, or any Ruby on Rails application errors requiring diagnosis. |
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
1. ActiveRecord::RecordNotFound
Symptoms: Rails fails to find a record in the database when using find or find_by! methods.
Diagnosis:
rails console
> Model.exists?(id)
> Model.where(conditions).count
> 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:
Model.find_by(id: params[:id])
def show
@record = Model.find_by(id: params[:id])
render_not_found unless @record
end
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
2. ActionController::RoutingError
Symptoms: 404 error - requested URL doesn't exist in the application.
Diagnosis:
rails routes
rails routes | grep resource_name
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:
Rails.application.routes.recognize_path('/your/path', method: :get)
resources :users, only: [:show, :index]
namespace :api do
resources :users
end
3. NoMethodError (undefined method for nil:NilClass)
Symptoms: Calling a method on nil object.
Diagnosis:
binding.break
debugger
byebug
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:
user&.profile&.name
user.profile || NullProfile.new
params[:key].presence || default_value
return unless user.present?
4. N+1 Query Problems
Symptoms: Slow page loads, excessive database queries in logs.
Diagnosis:
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:
User.includes(:posts, :comments).all
User.preload(:posts).find_each do |user|
user.posts.each { |post| ... }
end
User.joins(:posts).where(posts: { published: true })
belongs_to :user, counter_cache: true
5. Database Migration Failures
Symptoms: Migrations fail, schema out of sync, rollback errors.
Diagnosis:
rails db:migrate:status
rails db:abort_if_pending_migrations
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:
rails db:rollback
rails db:migrate
rails db:drop db:create db:migrate
rails runner "ActiveRecord::SchemaMigration.where(version: 'XXXXXX').delete_all"
gem 'strong_migrations'
6. ActionController::InvalidAuthenticityToken
Symptoms: CSRF token mismatch on POST/PUT/PATCH/DELETE requests.
Diagnosis:
<%= csrf_meta_tags %>
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>
skip_before_action :verify_authenticity_token, only: [:api_action]
protect_from_forgery with: :null_session
7. ActionView::Template::Error
Symptoms: View rendering fails with template errors.
Diagnosis:
<%= debug @variable %>
<%= @variable.inspect %>
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 %>
8. Asset Pipeline Issues
Symptoms: CSS/JS not loading, missing assets in production, Sprockets errors.
Diagnosis:
rails assets:precompile --trace
ls -la public/assets/
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:
Rails.application.config.assets.precompile += %w( custom.js custom.css )
<%= javascript_include_tag 'application' %>
<%= stylesheet_link_tag 'application' %>
<%= image_tag 'logo.png' %>
<%= javascript_importmap_tags %>
9. Gem Conflicts and Bundler Issues
Symptoms: Bundle install fails, version conflicts, LoadError.
Diagnosis:
bundle info gem_name
bundle viz
bundle outdated
bundle --version
Common Causes:
- Incompatible gem versions
- Platform-specific gems
- Missing native extensions
- Lockfile out of sync
Solutions:
bundle update gem_name
rm Gemfile.lock
bundle install
gem 'problematic_gem', '~> 1.0'
gem 'specific_gem', platforms: :ruby
10. NameError (Uninitialized Constant)
Symptoms: Ruby can't find class, module, or constant.
Diagnosis:
defined?(MyClass)
puts ActiveSupport::Dependencies.autoload_paths
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:
config.autoload_paths << Rails.root.join('lib')
require_relative '../lib/my_library'
Debugging Tools
Built-in Debug Gem (Rails 7+ / Ruby 3.1+)
The modern default debugger for Rails applications.
debugger
binding.break
binding.b
gem 'debug', group: [:development, :test]
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)
gem 'byebug', group: [:development, :test]
byebug
next, step, continue, quit
display @variable
where
Rails Console
rails console
rails c
rails console --sandbox
RAILS_ENV=production rails console
Useful Console Techniques:
reload!
ActiveRecord::Base.connection.execute("SELECT 1")
Benchmark.measure { Model.all.to_a }
ActiveRecord::Base.logger = Logger.new(STDOUT)
app.get '/path'
app.response.body
Better Errors Gem
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)
gem 'bullet', group: :development
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
gem 'pry-rails', group: [:development, :test]
binding.pry
ls
cd object
show-source method
The Four Phases of Rails Debugging
Phase 1: Reproduce and Isolate
Goal: Understand exactly what triggers the error.
git diff HEAD~5
git log --oneline -10
rails console
>
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.
Rails.logger.debug "Variable state: #{@variable.inspect}"
Rails.logger.tagged("DEBUG") { Rails.logger.info "Custom message" }
raise "Debug checkpoint"
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.
debugger
Model.find(id) rescue "Not found"
rails dbconsole
SELECT * FROM table WHERE condition;
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.
test "should handle missing record" do
assert_raises(ActiveRecord::RecordNotFound) do
get :show, params: { id: 0 }
end
end
rails test test/path/to_test.rb
rails server
Verification Checklist:
Quick Reference Commands
Routes
rails routes
rails routes -c users
rails routes -g api
rails routes | grep pattern
rails routes -E
Database
rails db:migrate:status
rails db:migrate
rails db:migrate VERSION=XXXXXX
rails db:rollback
rails db:rollback STEP=3
rails db:reset
rails db:seed
rails dbconsole
Rails Runner
rails runner "puts User.count"
rails runner "User.find(1).update(active: true)"
rails runner path/to/script.rb
RAILS_ENV=production rails runner "puts User.count"
Generators and Tasks
rails -T
rails generate --help
rails -D task_name
Cache
rails tmp:cache:clear
rails tmp:clear
Rails.cache.clear
Logs and Debugging
tail -f log/development.log
rails log:clear
tail -f log/development.log | grep -E "(ERROR|WARN)"
Testing
rails test
rails test test/models/user_test.rb
rails test test/models/user_test.rb:42
rails test -v
bundle exec rspec
bundle exec rspec spec/models/user_spec.rb
Console Tricks
reload!
User.all; nil
Benchmark.measure { Model.expensive_query }
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.logger = nil
require 'tracer'
Tracer.on { Model.method_call }
User.instance_method(:method_name).source_location
User.method(:class_method).source_location
Environment-Specific Debugging
Development
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.log_level = :debug
Production
RAILS_ENV=production rails console
> Rails.application.config.consider_all_requests_local = true
heroku logs --tail
ssh server 'tail -f /app/log/production.log'
Test
rails test -v
rails dbconsole -e test
RAILS_ENV=test rails db:migrate
Security Considerations
When debugging, be careful about:
Rails.logger.info params.except(:password, :credit_card)
Rails.application.config.filter_parameters += [
:password, :password_confirmation, :credit_card,
:cvv, :ssn, :secret, :token, :api_key
]
config.consider_all_requests_local = false
Additional Resources