Prevent an Infinite Loop When Using after_save in Rails

Infinite loops are fun, said no one ever.

If you're getting an error message like SystemStackError (stack level too deep) it means you have an infinite loop somewhere in your code. I'll briefly explain what's going on but you can skip to how to fix your after_save.

One of the most common causes for this is due to the after_save callback.

What's happening is you save a model, the after_save callback is triggered and runs some additional code that also saves the model, and now your callback is run again because it keeps saving the model, and on and on.

It's important to note, it's likely your changes save either because the database will rollback any pending changes:

   (0.3ms)  ROLLBACK
Traceback (most recent call last):
       16: from app/models/modal.rb:88:in `add_thing'
       15: from app/models/modal.rb:88:in `add_thing'
       14: from app/models/modal.rb:88:in `add_thing'
       13: from app/models/modal.rb:88:in `add_thing'
       12: from app/models/modal.rb:88:in `add_thing'
       11: from app/models/modal.rb:88:in `add_thing'
       10: from app/models/modal.rb:88:in `add_thing'
        9: from app/models/modal.rb:88:in `add_thing'
        8: from app/models/modal.rb:88:in `add_thing'
        7: from app/models/modal.rb:88:in `add_thing'
        6: from app/models/modal.rb:88:in `add_thing'
        5: from app/models/modal.rb:88:in `add_thing'
        4: from app/models/modal.rb:88:in `add_thing'
        3: from app/models/modal.rb:88:in `add_thing'
        2: from app/models/modal.rb:88:in `add_thing'
        1: from app/models/modal.rb:88:in `add_thing'
SystemStackError (stack level too deep)

How to Fix an after_save Infinite Loop

First, it depends on what specifically your after_save call needs to do.

If it changes a different model, you'll need to investigate that other model and look into its callbacks.

But the easiest way to come across this error is by calling an after_save that continually triggers itself. Specifically, the callback makes another update to the model that triggers another save.

So the easiest way to break this loop is to only make the update if the value changes.


update(computed_value: new_value) if computed_value != new_value

Ending a statement with a condition if statement like this is also known as a guard clause.

By using a guard clause on callbacks, you ensure that your update doesn't run if the value won't change. So if our after_save calls this update, it might run once, but the next time it will likely determine that the old and new values are the same so it won't trigger a save on the model and your callback will not loop forever.

Note that if your callback is doing anything dynamic like generating a random number or including timestamps, the old and new values always be different and still trigger updates and saves. Keep these exceptions in mind when designing your guard clause.

Level up faster
Recommended Books
Check out my list.
One on Ones: 101
Leveraging Other People's Experience
Hey, I'm Nicholas Dill.

I share everything I know about web development and SEO.

My agency is also offering free site audits for a limited time. Take me up on it while you can!

Related Articles
More like this
How to Render Plain Text Templates in Ruby on Rails
How to Fix "Address already in use – bind(2) for “” port 3000 (Errno::EADDRINUSE)"
How to Prevent Bot Spam on Your Ruby on Rails Website
Best Software Development Blogs -