AbstractController::DoubleRenderError Fix
Is this error giving you a headache? Have you already tried using and return
as Rails suggests in its guides but still can’t make the error go away?
TL;DR: See if you are calling render
in controller concerns. This post will explain why this might be the issue that’s right under your nose.
The key thing to understand here is that render
has multiple implementations in Rails and we need to know which one is being used when.
ActionView::Renderer#render
# rails/actionview/lib/action_view/renderer/renderer.rb# Main render entry point shared by Action View and Action Controller.def render(context, options)
if options.key?(:partial)
render_partial(context, options)
else
render_template(context, options)
end
end
In the context of a view, therender
method can be called multiple times to render partials. No specific restrictions apply to this implementation, so no exceptions will be raised in this case.
ActionController::Rendering#render
# rails/actionpack/lib/action_controller/metal/rendering.rb# Check for double render errors and set the content_type after rendering.def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if response_body
super
end
When explicitly calling render
from a controller, it checks that no other response_body
was previously set (e.g. via render
or redirect_to
). If there is already a response_body
, it raises AbstractController::DoubleRenderError
. On the other hand, if response_body
is empty, then everything works just fine.
Please note that explicitly calling render
in a controller action is not obligatory. In case this does not happen, then ActionController::ImplicitRender
will figure out which template to render for us.
So far so good. We now understand how the different implementations of render
work.
But, it can get tricky when trying to render partials from a controller concern. It’s true that we don’t see this very often and is probably not a good code design, but I’ve come across a situation like this when working on a project. Let’s look at the next simplified example.
Calling render inside a controller concern
Suppose there is a controller concern that exposes a helper_method
that renders a partial.
module Bar
extend ActiveSupport::Concern included do
helper_method :display_bar
end def display_bar
render partial: 'shared/bar'
end
end
Then, a controller includes this concern.
# app/controllers/foo_controller.rbclass FooController < ApplicationController
include Bar def index
end
end
And uses its helper_method
called display_bar
.
# app/views/foo/index.html.erb<%= display_bar %>
<h1>Home#index</h1>
<%= display_bar %>
Why would this raise ActionController::DoubleRenderError
? Are we not just trying to render partials multiple times? The answer would be: “Yes and no”.
If we had rendered the partials directly in the view, then no error would have been raised. That’s because we would have called ActionView::Renderer#render
and we know it has no restrictions.
But, since we are calling a helper_method
implemented in the context of a controller concern, the method will actually execute as part of the controller. It will therefore call ActionController::Rendering#render
, which we know cannot be explicitly called more than once.
Conclusion
Try not to call render
in controller concerns. But if you decide to do so, please be careful about ActionController::DoubleRenderError.