Fix: ActiveRecord::RecordNotFound — Safe Handling in Rails
Problem
Your Rails app crashes with a 404-style database error:
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=9999)
This shows the user a 500 error page or a raw exception — not ideal for production.
Symptoms
User.find(params[:id])raises exception when the record doesn’t exist- API returns 500 instead of 404
- Error tracking (Sentry, Bugsnag) floods with RecordNotFound exceptions
- Nested resource lookups fail silently or crash
Root Cause
Rails’ find method raises ActiveRecord::RecordNotFound when no record matches the given ID. Unlike find_by, which returns nil, find treats a missing record as an exceptional condition. The default Rails exception handler treats uncaught exceptions as 500 errors, which is semantically wrong for a missing resource.
Solution
Option 1: Use find_by with explicit nil check
# ❌ Crashes on missing record
@user = User.find(params[:id])
# ✅ Returns nil, handle gracefully
@user = User.find_by(id: params[:id])
if @user.nil?
redirect_to users_path, alert: "User not found."
return
end
Option 2: Rescue in the controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
private
def set_user
@user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to users_path, alert: "User not found."
end
end
Option 3: Global rescue with rescue_from
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
respond_to do |format|
format.html { render file: "#{Rails.root}/public/404.html", status: :not_found }
format.json { render json: { error: "Not found" }, status: :not_found }
end
end
end
This handles RecordNotFound across all controllers automatically.
Option 4: Use find_by! with rescue_from
find_by! raises RecordNotFound just like find, but lets you use non-ID columns:
@post = Post.find_by!(slug: params[:slug])
# Raises RecordNotFound if no post with that slug exists
Verification
# Test in console
User.find(9999)
# ActiveRecord::RecordNotFound: Couldn't find User with 'id'=9999
User.find_by(id: 9999)
# => nil
# With rescue_from set up, visit a non-existent record URL
# GET /users/99999
# Expect: 404 page, not a 500 error
Prevention
- Use
find_byfor lookup from user input — IDs from URLs are user input - Use
findonly when the record must exist (e.g., within a transaction after creating it) - Add
rescue_from ActiveRecord::RecordNotFoundto ApplicationController early in every project - Set up custom error pages for 404 and 500 in
public/404.htmlandpublic/500.html - Filter RecordNotFound from error trackers if you handle it with a 404 — it’s expected behaviour, not a bug
References
Advertisement