Ask Again Later Undefined Method for Nil nilclass
To give back to our community of developers, we looked at our database of thousands of projects and plant the superlative x errors in Red on Rails projects. We're going to prove you what causes them and how to prevent them from happening. If you lot avoid these "gotchas," it'll make yous a meliorate programmer.
Because data is king, we nerveless, analyzed, and ranked the elevation 10 Ruby errors from Ruby on Rails applications. Rollbar collects all the errors for each project and summarizes how many times each one occurred. We do this by grouping errors co-ordinate to fingerprinting. Basically, we group two errors if the 2nd ane is just a echo of the kickoff. This gives users a dainty overview instead of an overwhelmingly big dump like y'all'd run into in a log file.
We focused on the errors near probable to impact you and your users. To do this, we ranked errors by the number of projects experiencing them across different companies. We intentionally looked at the number of projects so that high-volume customers wouldn't overwhelm the information set up with errors that are non relevant to nigh readers.
Here are the meridian 10 Rail errors:
You've probably noticed some familiar faces in there already. Permit's dig in and take a look at the errors in a bit more detail to meet what might cause them in your production awarding.
We'll provide example solutions based on Runway 5, merely if yous're still using Track 4 they should point you in the right direction.
ane. ActionController::RoutingError
We start with a classic of any web application, the Rails version of the 404 fault. An ActionController::RoutingError
means that a user has requested a URL that doesn't exist within your application. Rails will log this and it will look similar an error, but for the most office it is not the fault of your application.
It may be acquired by incorrect links pointing at or from within your application. It may also be a malicious user or bot testing your application for common weaknesses. If that'due south the case, you lot might observe something like this in your logs:
ActionController::RoutingError (No route matches [GET] "/wp-admin"):
There is one mutual reason you lot might get an ActionController::RoutingError
that is caused by your awarding and not by errant users: if y'all deploy your awarding to Heroku, or any platform that doesn't permit you lot to serve static files, then you lot might find that your CSS and JavaScript doesn't load. If this is the instance, the errors will look like this:
ActionController::RoutingError (No route matches [GET] "/assets/awarding-eff78fd93759795a7be3aa21209b0bd2.css"):
To fix this and allow Rails to serve static assets you need to add a line to your application's config/environments/production.rb
file:
Rails.application.configure do # other config config.public_file_server.enabled = truthful stop
If you aren't interested in logging 404 errors caused by ActionController::RoutingError
and then you lot can avoid them by setting a catch all route and serving the 404 yourself. This method is suggested past the lograge projection{:target="_blank"}. To do so, add the following at the bottom of your config/routes.rb
file:
Runway.awarding.routes.draw practise # all your other routes match '*unmatched', to: 'application#route_not_found', via: :all end
Then add the route_not_found
method to your ApplicationController
:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception def route_not_found return file: Rail.public_path.join('404.html'), condition: :not_found, layout: false cease end
Before implementing this, you should consider whether knowing nearly 404 errors is important to y'all. You should also keep in mind that whatever route or engine that is mounted after the awarding loads won't be reachable{:target="_blank"} as they will be defenseless past the catch all route.
two. NoMethodError: undefined method '[]' for aught:NilClass
This ways that you lot are using square bracket notation to read a property from an object, but the object is missing, or cipher
, and thus it does not support this method. Since nosotros are working with square brackets, information technology'due south likely that nosotros're digging through hashes or arrays to access properties and something along the style was missing. This could happen when you're parsing and extracting information from a JSON API or a CSV file, or just getting data from nested parameters in a controller action.
Consider a user submitting address details through a form. You lot might look your parameters to look like this:
{ user: { address: { street: '123 Simulated Street', town: 'Faketon', postcode: '12345' } } }
You might then admission the street past calling params[:user][:accost][:street]
. If no address was passed and then params[:user][:address]
would exist nada
and calling for [:street]
would raise a NoMethodError
.
You could perform a nil check on each parameter and return early using the &&
operator, similar so:
street = params[:user] && params[:user][:address] && params[:user][:accost][:street]
While that will practice the job, thankfully at that place is now a better way to access nested elements in hashes, arrays and result objects similar ActionController::Parameters
. Since Cherry-red 2.3, hashes, arrays and ActionController::Parameters
have the dig
method{:target="_blank"}. dig
allows you to provide a path to the object you want to retrieve. If at any stage nil
is returned, and so dig
returns nix
without throwing a NoMethodError
. To get the street from the parameters in a higher place you tin can phone call:
street = params.dig(:user, :accost, :street)
You won't get whatsoever errors from this, though you practice need to exist enlightened that street
may still be nil
.
Equally an aside, if you are also excavation through nested objects using dot annotation, you tin can do this safely in Ruby two.three besides, using the safe navigation operator. So, rather than calling
street = user.address.street
and getting a NoMethodError: undefined method street
for cypher:NilClass
, you can now call.
street = user&.address&.street
The higher up will now deed the same every bit using dig
. If the address is cypher
then street
will be cypher
and y'all will need to handle the goose egg
when yous later refer to the street
. If all the objects are present, street
will be assigned correctly.
While this suppresses errors from being shown to the user, if it nevertheless impacts user experience, yous might want to create an internal error to track either in your logs or in an error tracking system like Rollbar and so y'all have visibility to fix the trouble.
If you are not using Ruddy two.iii or above you lot can achieve the aforementioned as higher up using the ruby_dig precious stone{:target="_blank"} and ActiveSupport's try
{:target="_blank"} to accomplish similar results.
3. ActionController::InvalidAuthenticityToken
Number three on our list requires conscientious consideration as information technology is related to our awarding's security. ActionController::InvalidAuthenticityToken
will be raised when a Postal service, PUT, PATCH, or DELETE asking is missing or has an incorrect CSRF (Cross Site Request Forgery) token.
CSRF is a potential vulnerability in web applications in which a malicious site makes a request to your application on behalf of an unaware user. If the user is logged in their session cookies will be sent along with the request and the attacker can execute commands equally the user.
Runway mitigates CSRF attacks{:target="_blank"} by including a secure token in all forms that is known and verified past the site, but can't be known past a third party. This is performed by the familiar ApplicationController
line
form ApplicationController < ActionController::Base protect_from_forgery with: :exception stop
Then, if your production application is raising ActionController::InvalidAuthenticityToken
errors it could mean that an attacker is targeting the users of your site, but the Track security measures are keeping you lot safe.
There are other reasons you may be unintentionally receiving this error though.
Ajax
For example, if y'all are making Ajax requests from your forepart, you lot need to ensure y'all are including the CSRF token within the request. If y'all are using jQuery and the congenital in Rails unobtrusive scripting adapter{:target="_blank"} and so this is already handled for you. If yous desire to handle Ajax another style, say using the Fetch API{:target="_blank"}, y'all'll need to ensure you include the CSRF token. For either approach, you need to make sure your application layout includes the CSRF meta tag in the <head>
of the document:
<%= csrf_meta_tags %>
This prints out a meta tag that looks like this:
<meta proper name='csrf-token' content='THE-TOKEN'>
When making an Ajax request, read the meta tag content and add information technology to the headers equally the X-CSRF-Token
header.
const csrfToken = document.querySelector('[name="csrf-token"]').getAttribute('content'); fetch('/posts', { method: 'POST', body: JSON.stringify(body), headers: { 'Content-Blazon': 'application/json' 'X-CSRF-Token': csrfToken } ).and then(role(response) { // handle response });
Webhooks/APIs
Sometimes there are valid reasons to plough off the CSRF protection. If you expect to receive incoming Mail requests to certain URLs in your application from third parties, you won't want to block them on the ground of CSRF. You might be in this position if you are building an API for third party developers or if yous expect to receive incoming webhooks from a service.
You can turn off CSRF protection, merely make sure you are whitelisting the endpoints you know don't need this kind of protection. Y'all can do and so in a controller by skipping the authentication:
form ApiController < ApplicationController skip_before_action :verify_authenticity_token end
If you lot are accepting incoming webhooks, you should be able to verify that the asking came from a trusted source in place of verifying the CSRF token.
4. Net::ReadTimeout
The Cyberspace::ReadTimeout
is raised when information technology takes Ruby longer to read data from a socket than the read_timeout
value, which is 60 seconds by default. This error tin be raised if you are using Net::HTTP
, open-uri
or HTTParty
{:target="_blank"} to make HTTP requests.
Notably, this doesn't mean that an mistake will exist thrown if the request itself takes longer than the read_timeout
value, just that if a particular read takes longer than the read_timeout
. You tin read more than about Net::HTTP
and timeouts from Felipe Philipp{:target="_blank"}.
At that place are a couple of things nosotros tin do to stop getting Internet::ReadTimeout
errors. In one case you empathise the HTTP requests that are throwing the error you tin can try to conform the read_timeout
value to something more sensible. As in the commodity above, if the server you are making the request to takes a long time to put together a response before sending it all at one time, you volition want a longer read_timeout
value. If the server returns the response in chunks and then y'all will want a shorter read_timeout
.
You can set read_timeout
past setting a value in seconds on the corresponding HTTP client you are using:
with Net::HTTP
http = Internet::HTTP.new(host, port, read_timout: 10)
with open-uri
open(url, read_timeout: ten)
with HTTParty
HTTParty.get(url, read_timeout: 10)
You lot can't always trust some other server to reply within your expected timeouts. If you can run the HTTP request in a background task with retries, like Sidekiq{:target="_blank"}, that can mitigate the errors from the other server. You volition need to handle the case where the server never responds in time though.
If you need to run the HTTP asking within a controller action, then you should be rescuing the Internet::ReadTimeout
fault and providing your user with an alternative experience and tracking it in your error monitoring solution. For example:
def show @post = Post.find(params[:slug]) begin @comments = HTTParty.become(COMMENTS_SERVER, read_timeout: 10) rescue Net::ReadTimeout => east @comments = [] @error_message = "Comments couldn't be retrieved, please try again later." Rollbar.error(eastward); end end
5. ActiveRecord::RecordNotUnique: PG::UniqueViolation
This error message is specifically for PostgreSQL databases, but the ActiveRecord adapters for MySQL and SQLite volition throw similar errors. The issue here is that a database tabular array in your application has a unique index on one or more fields and a transaction has been sent to the database that violates that alphabetize. This is a hard trouble to solve completely, but let's wait at the low hanging fruit first.
Imagine you've created a User
model and, in the migration, ensured that the user'southward e-mail accost is unique. The migration might wait like this:
form CreateUsers < ActiveRecord::Migration[5.1] def modify create_table :users do |t| t.string :e-mail t.timestamps cease add_index :users, :email, unique: truthful finish terminate
To avoid most instances of ActiveRecord::RecordNotUnique
you should add a uniqueness validation to your User
model too.
form User < ApplicationRecord validates_uniqueness_of :email end
Without this validation, all email addresses volition exist sent to the database when calling User#save
and will raise an error if they aren't unique. Even so, the validation tin can't guarantee that this won't happen. For a total caption you should read the concurrency and integrity department of the validates_uniqueness_of
documentation{:target="_blank"}. The quick description is that the Track uniqueness cheque is prone to race conditions based on the club of operation for multiple requests. Being a race condition, this also makes this mistake difficult to reproduce locally.
To bargain with this fault requires some context. If the errors are caused by a race status, that may be because a user has submitted a grade twice by mistake. We tin attempt to mitigate that issue with a flake of JavaScript to disable the submit button after the first click. Something a scrap like this is a offset:
const forms = document.querySelectorAll('form'); Assortment.from(forms).forEach((form) => { form.addEventListener('submit', (event) => { const buttons = course.querySelectorAll('push, input[type=submit]') Assortment.from(buttons).forEach((button) => { push.setAttribute('disabled', 'disabled'); }); }); });
This tip on Coderwall to use ActiveRecord's first_or_create!
along with a rescue and retry{:target="_blank"} when the fault is raised is a corking workaround. Y'all should go on to log the error with your error monitoring solution then that you maintain visibility on it.
def cocky.set_flag( user_id, flag ) # Making certain we just retry ii times tries ||= 2 flag = UserResourceFlag.where( :user_id => user_id , :flag => flag).first_or_create! rescue ActiveRecord::RecordNotUnique => e Rollbar.error(e) retry unless (tries -= 1).zero? end
ActiveRecord::RecordNotUnique
might seem like an border case, but it's here at number 5 in this pinnacle 10, and then it is definitely worth because with regard to your user experience.
6. NoMethodError: undefined method 'id' for nil:NilClass
NoMethodError
appears once again, though this fourth dimension with a different explanatory message. This fault usually sneaks up around the create action for an object with a relation. The happy path—creating the object successfully—ordinarily works, but this error pops upwards when validations fail. Let's have a look at an example.
Here's a controller with actions to create an application for a course.
class CourseApplicationsController < ApplicationController def new @course_application = CourseApplication.new @grade = Grade.notice(params[:course_id]) terminate def create @course_application = CourseApplication.new(course_application_params) if @course_application.save redirect_to @course_application, notice: 'Application submitted' else render :new end end private def course_application_params params.require(:course_application).allow(:name, :email, :course_id) cease cease
The grade in the new template looks a bit like this:
<%= form_for [@grade, @course_application] do |ca| %> <%# residuum of the course %> <% end %>
The problem hither is when you call return :new
from the create
activeness, the @course
instance variable wasn't set. Y'all demand to ensure that all the objects the new
template needs are initialised in the create
action as well. To fix this fault, nosotros'd update the create
action to look like this:
def create @course_application = CourseApplication.new(course_application_params) if @course_application.save redirect_to @course_application, notice: 'Application submitted' else @course = Course.notice(params[:course_id]) render :new end end
Check out this article if y'all are interested in learning more about the bug with nil
in Rails and how to avert them{:target="_blank"}.
7. ActionController::ParameterMissing
This error is part of the Track stiff parameters{:target="_blank"} implementation. It does not manifest every bit a 500 error though—it is rescued by ActionController::Base
and returned as a 400 Bad Request.
The full error might wait like this:
ActionController::ParameterMissing: param is missing or the value is empty: user
This will be accompanied by a controller that might look a scrap like this:
class UsersController < ApplicationController def create @user = User.new(user_params) if @user.relieve redirect_to @user else render :new end terminate private def user_params params.crave(:user).permit(:name, :electronic mail) end stop
The params.require(:user)
means that if user_params
is called and params
does not have a :user
key or params[:user]
is empty, ActionController::ParameterMissing
will be raised.
If you are building an application to be used via a web forepart end and yous have built a form to correctly post the user
parameters to this activity, then a missing user
parameter probably means someone is messing with your application. If that is the example, a 400 Bad Request response is likely the best response as you don't need to cater to potentially malicious users.
If your awarding is providing an API, then 400 Bad Request is also an appropriate response to a missing parameter.
viii. ActionView::Template::Fault: undefined local variable or method
This is our but ActionView
error in the peak ten and that'due south a good sign. The less piece of work the views have to do to render templates the better. Less work leads to fewer errors. Nosotros're withal left with this mistake though, in which a variable or method you await to be just doesn't.
This crops upwards most commonly in partials, probably due to the many dissimilar ways you lot can include a fractional with local variables on a page. If you take a partial chosen _post.html.erb
that contains a web log post template and an instance variable @mail service
prepare in your controller, so you can render the partial like this:
<%= return @post %>
or
<%= render 'post', postal service: @post %>
or
<%= render fractional: 'post', locals: { mail: @post } %>
Runway likes to give u.s.a. plenty of options to piece of work with, only the second and third options hither are where confusion tin can creep in. Trying to render a fractional like:
<%= return 'post', locals: { mail service: @post } %>
or
<%= render partial: 'post', post: @post %>
will leave you with an undefined local variable or method. To avoid this, stay consistent and e'er render partials with the explicit partial syntax, expressing the local variables in a locals hash:
<%= return fractional: 'post', locals: { mail service: @postal service } %>
There is 1 other identify you can skid up with local variables in partials. If yous only sometimes pass a variable to a partial, testing for that variable is different within a fractional to regular Ruby code. If, for example, you update the mail fractional higher up to take a local variable that tells you whether to show a header image in the partial, you would render the partial similar so:
<%= render fractional: 'postal service', locals: { post: @post, show_header_image: truthful } %>
Then the partial itself might wait like this:
<h1><%= @post.title %></h1> <%= image_tag(@post.header_image) if show_header_image %> <!-- and and then on -->
This will work fine when you pass the show_header_image
local variable, but when you call
<%= return partial: 'post', locals: { post: @post } %>
it volition neglect with an undefined local variable. To test for the being of a local variable inside a fractional, you should check whether it is defined before you use information technology.
<%= image_tag(@post.header_image) if defined?(show_header_image) && show_header_image %>
Even ameliorate though, there is a hash called local_assigns
within a fractional that we tin employ instead.
<%= image_tag(@post.header_image) if local_assigns[:show_header_image] %>
For variables that aren't booleans, we tin can use other hash methods like fetch
to handle this gracefully. Using show_header_image
as an example, this scenario would also work:
<%= image_tag(@post.header_image) if local_assigns.fetch(:show_header_image, faux) %>
Overall, sentinel out when you lot are passing variables to partials!
nine. ActionController::UnknownFormat
This error, like the ActionController::InvalidAuthenticityToken
, is one that could be caused by careless or malicious users rather than your awarding. If you've built an application in which the actions respond with HTML templates and someone requests the JSON version of the page, y'all will find this mistake in your logs, looking a bit like this:
ActionController::UnknownFormat (BlogPostsController#alphabetize is missing a template for this request format and variant. request.formats: ["awarding/json"] asking.variant: []):
The user will receive a 406 Not Acceptable response. In this example they'll see this mistake because you haven't divers a template for this response. This is a reasonable response, since if y'all don't want to return JSON, their asking was non adequate.
You may, however, have congenital your Rails awarding to reply to regular HTML requests and more than API-like JSON requests in the same controller. Once you start doing this, you define the formats you do want to respond to and any formats that fall exterior of that will also cause an ActionController::UnknownFormat
, returning a 406 status. Let'south say you have a blog posts index that looks like:
form BlogPostsController < ApplicationController def index respond_to practice |format| format.html { render :index } terminate cease end
Making a request for the JSON would outcome in the 406 response and your logs would prove this less expressive error:
ActionController::UnknownFormat (ActionController::UnknownFormat):
The error this fourth dimension doesn't complain nigh a lack of a template—it's an intentional mistake because you have divers the only format to respond to is HTML. What if this is unintentional though?
Information technology's mutual to miss a format in a response that you intend to support. Consider an action in which y'all desire to reply to HTML and JSON requests when creating a blog mail, so that your page can back up an Ajax request. It might look like this:
form BlogPostsController < ApplicationController def create @blog_post = BlogPost.new(blog_post_params) respond_to do |format| if @blog_post.salve format.html { redirect blog_post_path(@blog_post) } format.json { render json: @blog_post.to_json } else render :new cease stop end end
The fault here is raised in the case of the blog postal service failing validations and not saving. Within the respond_to
block, yous need to call render
within the scope of the format blocks. Rewriting this to accommodate for failure would look like:
class BlogPostsController < ApplicationController def create @blog_post = BlogPost.new(blog_post_params) respond_to practise |format| if @blog_post.salve format.html { redirect blog_post_path(@blog_post) } format.json { return json: @blog_post.to_json } else format.html { render :new } format.json { render json: @blog_post.errors.to_json } end end end cease
At present all of the formats are covered and there won't be any more than unintentional ActionController::UnknownFormat
exceptions.
10. StandardError: An fault has occurred, this and all afterwards migrations canceled
This final particular on our tiptop 10 disappoints me slightly. StandardError
{:target="_blank"} is the base of operations error class that all other errors should inherit from, so using it here makes the fault feel very generic, when in reality it is an error that has happened during a database migration. I would prefer to come across this fault as a descendent of the ActiveRecord::MigrationError
. But I digress…
At that place are a number of things that can cause a migration to fail. Your migrations may have gotten out of sync with your actual production database, for case. In that case, you're going to have to go excavation around to find out what has happened and set up information technology.
In that location is ane thing that should be covered hither though: information migrations.
If you need to add together or calculate some data for all the objects in a table y'all might think that a data migration is a good idea. As an case, if you wanted to add a full proper noun field to a user model that included their showtime and terminal name (not a likely modify, only expert enough for a uncomplicated example), y'all might write a migration like this:
course AddFullNameToUser < ActiveRecord::Migration def up add_column :users, :full_name, :string User.find_each exercise |user| user.full_name = "#{user.first_name} #{user.last_name}" user.save! end end def downwards remove_column :users, :full_name terminate end
There are a lot of problems with this scenario. If in that location is a user with corrupt data in y x xour set, the user.save!
command will throw an error and abolish the migration. Secondly, in production y'all may have a lot of users, which ways the database would have a long time to migrate, peradventure keeping your application offline for the entire fourth dimension. Finally, as your application changes over fourth dimension, you might remove or rename the User
model, which would crusade this migration to fail. Some advice suggests that you ascertain a User
model inside the migration to avoid this. For even greater safety, Elle Meredith advises united states of america to avoid information migrations inside ActiveRecord migrations{:target="_blank"} completely and build out temporary data migration tasks instead.
Changing information outside of the migration ensures you do a few things. Most importantly, it makes y'all consider how your model works if the data is not present. In our full name example, you would likely define an accessor for the full_name
property that could reply if the data was available. If information technology's non, and so build the total name by concatenating the constituent parts.
form User < ApplicationRecord def full_name @full_name || "#{first_name} #{last_name}" end cease
Running a data migration as a separate task also means the deploy no longer relies on this data changing beyond the production database. Elle'south article has more reasons why this works better and includes best practices on writing the task as well.
Determination
The most popular Runway errors can come up from anywhere inside the application. In this article we've seen common errors manifested in the model, the view, and the controller. Some of the errors aren't necessarily anything to worry about and are simply protecting your application. Others should be caught as presently every bit possible and stamped out.
Nevertheless, information technology'due south proficient to rail how oftentimes these errors happen. This leads to improve visibility into bug that are affecting your users or application security, so you tin ready them quickly. Otherwise, these error messages will exist shown to the user, simply the technology and production management teams will take no thought until users complain to support.
We promise you learned something new and are better equipped to avoid these errors in the future. However, even with the best practices, unexpected errors do pop upwards in production. It'south important to have visibility into errors that affect your users, and to have proficient tools to solve them quickly.
Rollbar gives y'all visibility to product Crimson errors, which offers more than context to solve them quickly, including form validation errors, person tracking, kicking errors and more. Check out Rollbar'southward full listing of features and Ruby-red SDK documentation.
If you oasis't already, sign up for a 14-day free trial of Rollbar and let us help you take command of impactful Rail errors.
Source: https://rollbar.com/blog/top-10-errors-from-1000-ruby-on-rails-projects-and-how-to-avoid-them/
0 Response to "Ask Again Later Undefined Method for Nil nilclass"
Post a Comment