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

Standard Case

def show
  if params[:type] == 'home'
    redirect_to home_path
  end
  redirect_to user_path
end

The code above will produce the DoubleRenderError if 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 DoubleRenderError.

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 redirect_to or render is called within the before_action.

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 return statement from within a called method does not stop execution in the calling method (the action).
  • Remember that a redirect_to/render inside of a before_action prevents the action from being called.