A quick guide to model errors in Rails

Ricardo Fleury
2 min readFeb 21, 2017

--

When initialized, every Rails model inheriting from ActiveRecord::Base implicitly instantiates an ActiveModel::Errors object. That object is accessible via the #errors method on the model’s instance and contains the errors generated when the model is validated on a create or update action. The presence of any errors prevents the model from being saved successfully, returning false from #save or an exception (ActiveRecord::RecordInvalid) from #save!.

Let’s dive into the structure of the ActiveModel::Errors object, how we can add and customize errors, and then format them for display.

For the example, we have a Shipment model with some standard validations:

class Shipment < ActiveRecord::Base
...
validates_presence_of :weight, :height, :depth, :width,
message: “needs to exist in order to have a box!”
validates_numericality_of :weight, :height, :depth, :width ...
end

The errors are formatted as a hash with attributes as the keys and arrays of error messages as the hash values. We can access the unformatted messages hash by using the #messages method on the errors object. Note that we used the :message option to customize the message coming from the presence validation.

shipment = Shipment.create(weight: 2, height: 2, width: “boo”)
shipment.errors.messages
=> {
:depth=>[“needs to exist in order to have a box!”, “is not a number”],
:width=>[“is not a number”]
}

For custom validations (here’s a post detailing custom validation strategies), my preferred way to add errors manually with the #add method. If the error is not confined to an attribute, we may choose to assign another descriptive term or default to :base. Here are ways to add errors manually:

shipment.errors.add(:base, “is a not valid generic message”)
shipment.errors[:base] = “is a not valid generic message”

After the basic validation and these new errors, we get the following #messages hash:

shipment.errors.messages
=> {
:depth=>[“needs to exist in order to have a box!”, “is not a number”],
:width=>[“is not a number”],
:base=>[“is a not valid generic message”]
}

Now that we have the messages as a hash, the errors object exposes a few useful methods to sanitize and access additional error information. Here are some examples based on the errors above:

# full_messages
shipment.errors.full_messages
=> [“Depth needs to exist in order to have a box!”, “Depth is not a number”, “Width is not a number”, “is a not valid generic message”]
# full_messages_for
shipment.errors.full_messages_for(:depth)
=> [“Depth needs to exist in order to have a box!”, “Depth is not a number”]
# get — gets only the messages without the keys
shipment.errors.get(:depth)
=> [“needs to exist in order to have a box!”, “is not a number”]
# include?
shipment.errors.include(:depth)
=> true
# empty?
shipment.errors.empty?
=> false
# clear — clears all errors
shipment.errors.clear
shipment.error.messages
=> []

Thank you for reading. I appreciate any feedback that helps be a better writer and communicator, so please don’t hesitate to comment. Also open to future topics. Find my other posts on my Medium page.

--

--

Ricardo Fleury

Web developer looking to learn and teach. Brasileiro. Soccer junkie.