Fix ActiveRecord::RecordNotFound: Couldn't find User with 'id'=999 in Rails
This error occurs when ActiveRecord's find method is called with an ID that does not exist in the database. Rails raises RecordNotFound which renders a 404 in production but shows a stack trace in development. Fix it by rescuing the exception in the controller or using find_by with a nil check for custom error handling.
Reading the Stack Trace
Here's what each line means:
- Couldn't find User with 'id'=999: The database has no User record with id=999. This is typically caused by a stale link, deleted record, or invalid URL parameter.
- app/controllers/users_controller.rb:8:in `show': The show action at line 8 calls User.find(params[:id]) with an ID that does not exist.
- lib/action_controller/metal/rescue.rb:22:in `process_action': Rails rescue middleware can catch this exception if you configure rescue_from in the controller.
Common Causes
1. Deleted or non-existent record
A user was deleted but links to their profile still exist, or a user manually enters an invalid ID in the URL.
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
end
2. Stale cache or bookmark
A cached page or bookmarked URL references a record that has since been deleted from the database.
# URL: /users/999 (bookmarked months ago, user was deleted since)
class UsersController < ApplicationController
def show
@user = User.find(params[:id]) # Raises RecordNotFound for deleted user
end
end
The Fix
Add a rescue_from handler in ApplicationController that catches RecordNotFound and renders a proper 404 page. This handles the error gracefully across all controllers without modifying each action individually.
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
end
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render file: Rails.public_path.join('404.html'), status: :not_found, layout: false
end
end
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
end
Testing the Fix
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
describe 'GET #show' do
context 'when the user exists' do
let(:user) { User.create!(name: 'Alice', email: 'alice@example.com') }
it 'returns 200' do
get :show, params: { id: user.id }
expect(response).to have_http_status(:ok)
end
it 'assigns the user' do
get :show, params: { id: user.id }
expect(assigns(:user)).to eq(user)
end
end
context 'when the user does not exist' do
it 'returns 404' do
get :show, params: { id: 999 }
expect(response).to have_http_status(:not_found)
end
end
end
end
Run your tests:
rspec
Pushing Through CI/CD
git checkout -b fix/record-not-found-handler,git add app/controllers/application_controller.rb spec/controllers/users_controller_spec.rb,git commit -m "fix: add global rescue_from for RecordNotFound to return 404",git push origin fix/record-not-found-handler
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: test_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- run: bundle exec rails db:create db:migrate
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db
RAILS_ENV: test
- run: bundle exec rspec
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db
RAILS_ENV: test
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
gem install bugstack
Step 2: Initialize
require 'bugstack'
Bugstack.init(api_key: ENV['BUGSTACK_API_KEY'])
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Run rspec locally to confirm 404 responses for missing records.
- Open a pull request with the rescue_from handler.
- Wait for CI checks to pass on the PR.
- Have a teammate review and approve the PR.
- Merge to main and verify the 404 page renders in staging.
Frequently Asked Questions
BugStack runs specs for all controllers, verifies RecordNotFound returns 404 consistently, and checks that valid records still load correctly.
All fixes are submitted as pull requests with full CI checks. Your team reviews the rescue_from logic before merging.
Yes, Rails shows a 404 page in production by default. But adding rescue_from gives you control over the response format, which is important for JSON APIs.
For APIs, find with rescue_from is cleaner because it centralizes error handling. find_by with manual nil checks works but adds boilerplate to every action.