rails: Defining a `config` action on a controller causes a `SystemStackError`
Steps to reproduce
-
Generate a new Rails app (example Rails 5.2 app, example Rails 6.1 app)
rails new config-demo --minimal
-
Create a new controller with two actions, with at least one named
config
, which render something simpleclass MyConfigController < ApplicationController def notconfig render text: :notconfig end def config render text: :config end end
-
Create routes for the new controller and actions
get "/config, to: "my_config#config" get "/notconfig, to: "my_config#notconfig"
-
Create a new ActionDispatch test file that calls the new routes
require "test_helper" class MyConfigControllerTest < ActionDispatch::IntegrationTest def test_notconfig get "/notconfig" assert_equal 200, status end def test_config get "/config" assert_equal 200, status end end
-
Run the test
bin/rails test
Expected behavior
I would expect the routes to work as intended, or for Rails to generate an error about overriding the config
method as an action.
Actual behavior
Both new routes result in a SystemStackError: stack level too deep
pointing to the config
action definition. This is because ActiveSupport
defines ActiveSupport::Configurable#config
which is included into ActionController::Base
by default, and the new config
action overrides it. (We can confirm this by adding a byebug breakpoint in the config
action and calling method(:config).super_method
from the debugger console.) Our actions both call render
which eventually will call logger
which is a configurable attribute which calls config
which then calls the new config
action which calls render
and so on. I initially spotted this when a team mate tried to create a config
endpoint using https://github.com/github/chatops-controller, which essentially works the same way by defining a new action on the controller.
System configuration
Rails version: 5.2.4.4, 6.1.1, 6.2.0.alpha
Ruby version: 2.7.1, 2.6.6
Updated to show that this affects other actions in the same controller and does not rely on calling the config action directly
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 23 (20 by maintainers)
https://github.com/rails/rails/blob/main/actionpack/lib/abstract_controller/base.rb#L156-L297 is a good start, but there are many others in
ActionController::Metal
andActionController::Base
.@chrisbloom7 I need a bit more time to review this, but unfortunately yes a PR will make things easier once I can see the code 🙇 Thanks for your effort!
I wanted to get a sense of how many methods would result in something like what redefining
config
as an action method does, mainly to see if the list would be small enough to include in full in the new callout section. To help figure that out I generated a new rails app that dynamically creates controllers, actions, and routes for each instance method onActionController::Base
, with an integration test suite with a custom reporter that would exercise each route and dump failures to a CSV file. In Rails 6.1.3 there are 17 methods (includingconfig
) that would result inSystemStackError: stack level too deep
if redefined in a controller, 9 that would result in aArgumentError
if redefined, and 3 that would result in aAbstractController::DoubleRenderError
. Most of these are obvious, like yeah don’t redefineclass
orrender
oraction_name
. TBH the only one that jumps out at me as surprising is the one I originally encountered:config
. So it’s probably not worth mentioning specific method names, but I still feel it’s worth calling out that there is a potential for accidentally redefining one of these methods and suddenly getting hit with ambiguousSystemStackError
in all actions in the controller. Working on something along the lines of the text below. Will refine it and get a PR up in the morning@zzak thanks for the reminder. I haven’t yet, but I can get to it this week.