I'm often guilty of producing this error from my Rails controllers:
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action.
I know the gist of why it occurs, but there are a few wrinkles that seem to trip me up. Time to iron those out.
The basic notion is that we cannot render and/or redirect more than once in a controller action. Okay, that makes sense. It is easy to assume that a
redirect_to statement would end the execution of an action, but it doesn't. The easiest case to fix is when there are multiple redirect_to or renders just within the action itself
def show if params[:type] == 'home' redirect_to home_path end redirect_to user_path end
The code above will produce the
params[:type] == 'home'. We can fix this by adding an explicit return after the redirect:
redirect_to home_path and return, or by wrapping the rest of the action in an
else clause. Either way will work.
Private Method Case
The next more devious case is from within a method called from the action.
def show redirect_if_home(params[:type]) redirect_to user_path end private def redirect_if_home(type) if type == 'home' redirect_to home_path and return end end
This seems to be a nice refactoring, but now the
and return statement just returns from the
redirect_if_home method, and we still get the
Before Action Case
The final example is with
before_action calls. If you've been bitten by the above example, and are paying attention to
redirect_to within a private method, it's easy to fall into this final case:
before_action :redirect_if_home def show redirect_to user_path end private def redirect_if_home if params[:type] redirect_to home_path and return end end
This works, but the
and return is unnecessary. Why, what's the difference with and without the
before_action. It turns out that
before_action will cancel the execution of the action if a
render is called within the
Here is the documentation that describes that behavior https://guides.rubyonrails.org/action_controller_overview.html#filters
If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled.
Keep these three principles in your head when thinking about or fixing a DoubleRenderError:
- Remember that a redirect_to/render does not stop execution in a controller action
- Remember that adding an
and returnstatement from within a called method does not stop execution in the calling method (the action).
- Remember that a
renderinside of a
before_actionprevents the action from being called.