To us programmers, errors are a way of life. We work hard to make sure they don't happen, design them so they provide useful information, wrestle with them until the wee hours of the morning, and (with enough experience) learn to appreciate them.
That doesn't make dealing with them any less frustrating, however. Let's dive into some common Rails errors and explore not only how to fix them, but why they are being raised in the first place.
How to Fix ActionController::RoutingError
- Check that you have the correct route defined in
config/routes.rb
. You may have missed the definition! - You may have the wrong HTTP verb coming from the client.
- Look in your console for a log like
Started GET "/things" for ::1 at 2025-05-06 22:46:18 -0700
. This will let you know exactly what verb is being used.
- Look in your console for a log like
- Check your path and URL helpers. You may want to run
rails routes
and check theprefix
column for the name of the base route helper methods. - If you are seeing this when trying to serve images or static assets, ensure that you are using the correct asset helper.
image_tag('logo.png')
— Generates an<img>
HTML tag for an image asset.image_path('logo.png')
— Returns the URL path to an image asset, without generating an HTML tag.asset_path('logo.png')
— General method for any asset (not just images), returning the relative path.asset_url('logo.png')
— Returns the full absolute URL to the asset (including host if configured).
If you are stuck, or need a fallback option, there is a way to add a 'catch all' route to handle failures more gracefully. ⚠️ Make sure this route is defined at the very bottom of your
config/routes.rb
file so it doesn’t interfere with valid routes. ⚠️match '*path', to: 'application#not_found', via: :all
What is the purpose of ActionController::RoutingError
?
ActionController::RoutingError
is Rails' way of making sure every request coming into your app has a place to go, and that place has to be explicitly defined. It’s there to protect your app from bots poking around where they shouldn't be, to make sure you’re only exposing what you actually intend to, and to stick to Rails' philosophy of convention over configuration. It keeps your app predictable, secure, and a whole lot easier to maintain as it grows.
How to Fix NoMethodError: undefined method '[]' for nil:NilClass
If you are calling this on a
Hash
, you are probably trying to access a key that doesn't exist. This commonly happens when trying to access nested keys inparams
. Try using#dig
to safely access nested hashes!params = {} admin = params[:admin][:name] #=> NoMethodError: undefined method '[]' for nil:NilClass params = { admin: { name: "Shrek" } } admin = params.dig(:admin, :name) #=> "Shrek"
You may be trying to call the
#[]
method on an object that doesn't have it defined. This commonly happens withnil
. You can use the Safe Navigation Operator (&.
) to safely call methods even when an object might benil
.You may have a
nil
value! Try to be defensive and ensure that things are notnil
.if admin.present? greeting = "Hello, #{admin.name}!" else greeting = "Hello, guest!" end # Or with a strong default... greeting = "Hello, #{admin&.name || 'Shrek'}!"
Whenever you hit a
NoMethodError
involvingnil
, it’s a signal to double-check your assumptions about your data and not just patch the crash.
What is the purpose of NoMethodError: undefined method '[]' for nil:NilClass
?
NoMethodError: undefined method '[]' for nil:NilClass
is how Rails tells you that there is no method (#[]
in this case) on the object you are trying to invoke it on. This is part of Ruby’s fail fast
philosophy: it surfaces problems right away, instead of letting bugs hide and cause weird behavior later.
How to Fix ActionController::InvalidAuthenticityToken
Make sure that you include CSRF meta tags in your layout. This automatically sets the CSRF token that Rails uses to make
PUT
,POST
andPATCH
requests.<%= csrf_meta_tags %>
Use Rails form helpers! They are both convenient and powerful. They will automagically add the CSRF token to the form.
<%= form_with model: @post do |f| %> ... <% end %> # If you don't have a model for a form, you can always use: <%= form_tag '/some_path' do %> ... <% end %>
If you are manually writing HTML forms, you can also add the CSRF token to it manually. Generally, it's advised not to do this as you miss out on a lot of important Rails magic!
<form action="/some_path" method="post"> <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>"> ... </form>
If you are making AJAX requests, you need to set the
X-CSRF-Token
header using the Rails-generated CSRF token. If you are using Rails UJS (Unobtrusive JS), Rails will set this automatically. If you are using your own (like jQuery, Fetch, or Axios) you will need to retrieve the value and set it:const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); fetch('/posts', { method: 'POST', headers: { 'X-CSRF-Token': token, 'Content-Type': 'application/json' }, body: JSON.stringify({ post: { title: "New Post" } }) });
There may be an expired session which can be from it expiring naturally, cookies being cleared, or it becoming corrupted. If you have narrowed it down to a session problem and are not being redirected automatically, it could be that they are not being redirected back to the login page correctly. Check your controllers and make sure that something hasn't been overridden or misconfigured.
What is the purpose of ActionController::InvalidAuthenticityToken
?
If you see InvalidAuthenticityToken
, it's not just a bug! It's Rails stepping in to protect you. It's a chance to double-check how you're building forms, handling sessions, or sending requests. Rails is trying to keep you and your users safe.
⚠️ In select cases you may want to skip CSRF authentication. Be really sure before skipping CSRF protection. If you do, it's your responsibility to lock the endpoint down another way. Valid use-cases could be a public API endpoint, Webhook endpoints, or internal services. ⚠️
class PublicFacingController < ApplicationController
skip_before_action :verify_authenticity_token # Above other auth calls
before_action :verify_api_key # Custom authentication
def update
Admin.update!(name: params.dig(:admin, :name))
end
end
How to Fix ActiveRecord::RecordNotFound
Handle it gracefully! If you are working with controllers, you can use
rescue_from
to handle this behavior. This is very common when designing APIs, as you can :# Normal Rails controller with a 'show' view. class AdminController < ApplicationController rescue_from ActiveRecord::RecordNotFound, with: :not_found def show @admin = Admin.find(params[:id]) end private def not_found render file: Rails.root.join('public/404.html'), status: :not_found, layout: false end end # API Version where it's used as a 'catch' so the correct response can be given back. class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, with: :render_not_found private def render_not_found render json: { error: 'Not Found' }, status: :not_found end end
Use a safe finder method! You may be using
#find
when it's OK if the record doesn't exist. You will want to use a method like#find_by
instead.admin = Admin.find(42) # => ActiveRecord::RecordNotFound (Couldn't find Admin with 'id'=42) admin = Admin.find_by(id: 42) # => nil # Remember, find_by works with any attribute and will return the first matching record! admin = Admin.find_by(name: 'Shrek') # => #<Admin id: 7, name: "Shrek", email: "shrek@railsmaze.com", created_at: "2025-05-06 23:10:15", updated_at: "2025-05-06 23:10:15">
What is the purpose of ActiveRecord::RecordNotFound
?
It's greatest strength is that it provides protection against bad assumptions. You don't want to accidentally authorize a nil
record for an Admin
! It also reduces subtle bugs due to nil
assumptions. By raising an error immediately when something is missing, Rails forces you to handle edge cases intentionally, not leave them wide open to exploitation.
How to Fix ActiveRecord::RecordNotUnique
Validate for uniqueness on the model. This is useful as you can provide users with a meaningful feedback message instead of a generic error. You can enforce uniqueness like this:
class Admin < ApplicationRecord validates :email, uniqueness: { case_sensitive: false } end
Validate for uniqueness on the database. This can be considered mandatory in many cases for columns like emails, phone numbers, unique slugs, and sensitive payment information.
class AddEmailToAdmins < ActiveRecord::Migration[8.0] def change # Add the email column first add_column :admins, :email, :string # Then add a unique index on the email column add_index :admins, :email, unique: true end end
You can also
rescue
the error. This is particularly handy in API contexts.begin Admin.create!(email: params[:email]) rescue ActiveRecord::RecordNotUnique render json: { error: "That email address is already taken." }, status: :unprocessable_entity end
What is the purpose of ActiveRecord::RecordNotUnique
?
Rails tries to be nice to users. PostgreSQL protects your data when users (or your app) aren’t nice. Always validate uniqueness at the Rails level for a friendly experience. Always enforce it at the database level for true safety. Failing fast with RecordNotUnique
prevents ghost
users, duplicate purchases, impersonations, and billing errors.
Conclusion
Whether it's a missing route, a method call on nil
, a missing database record, or a uniqueness violation, errors in Rails aren't just obstacles. They’re feedback loops, designed to help you catch problems early, protect your users, and keep your application stable as it grows.
- Every
RoutingError
reminds you to be explicit about your app’s structure. - Every
NoMethodError
challenges you to handle missing data carefully. - Every
RecordNotFound
enforces the idea that you can't assume the world will always behave the way you want. - Every
RecordNotUnique
protects your data — and your business — from collapsing under concurrency.
Instead of fighting Rails when it raises these errors, it's better (and honestly more fun) to listen carefully to what it's trying to tell you.
Each failure is an opportunity to make your system stronger, clearer, and more resilient.
Treat your errors like mentors. They aren't the enemy — they're trying to make you a better developer.