This project is archived and is in readonly mode.

#6275 ✓duplicate
Kenn Ejima

AbstractController::ActionNotFound should be rescued

Reported by Kenn Ejima | January 10th, 2011 @ 10:33 PM

Suppose I have the following routes.rb:

match ':controller/:action', :controller => /api\/(room|user)/
match 'api/*others', :to => 'api/base#render_404_as_json'

and controllers:

class Api::BaseController
  def render_404_as_json
    render :text => '{"status":"error","code":"invalid_api_method"}'

class Api::RoomController < Api::BaseController

class Api::UserController < Api::BaseController

Now, when "/api/bogus" gets hit, the request is routed to render_404_as_json as it should.

However, when "/api/room/bogus" gets hit, it raises AbstractController::ActionNotFound, even if I had the following lines in api/base_controller.rb or application_controller.rb.

rescue_from Exception, :with => :render_500_as_json
rescue_from AbstractController::ActionNotFound, :with => :render_404_as_json

My goal here is to never return an unstructured HTML response of 404 for any URL under /api.

I think we have two ways to get around this:

  • AbstractController::ActionNotFound should be raised inside RoomController so that you can catch it and do your own stuff, or
  • When AbstractController::ActionNotFound is raised, continue down the routing matches (in the example above, 'api/*others' in the next line matches)

Comments and changes to this ticket

  • Andrew White

    Andrew White February 9th, 2011 @ 04:24 AM

    • State changed from “new” to “duplicate”
    • Importance changed from “” to “Low”

    This is essentially a duplicate of #4444. You've almost got the workaround from that ticket but your problem is that the first route is partially matching on the controller. If you add an :action regexp constraint listing the valid actions it should fall through to the second route.

  • Kenn Ejima

    Kenn Ejima February 23rd, 2011 @ 10:16 PM

    Thanks Andrew, patching render_exception worked as you said. In my case:

    require 'action_dispatch/middleware/show_exceptions'
    module ActionDispatch
      class ShowExceptions
        def render_exception_with_template(env, exception)
          if exception.kind_of? AbstractController::ActionNotFound
            request =
            if request.env['action_dispatch.request.path_parameters'][:controller] =~ /^api\/.+/
              return Api::BaseController.action(:render_404_as_json).call(env)
          render_exception_without_template(env, exception)
        alias_method_chain :render_exception, :template

    I have 20 controllers and 100+ actions under the /api context so listing them all in the :action regexp constraint seemed like a bad idea.

  • Kevin Watt

    Kevin Watt March 19th, 2011 @ 01:35 AM

    I'm trying your solution, but don't seem to have access to MyApp::HomeController in this context. Any tips? Is it in any strange namespaces besides the one I define in application.rb?

  • Andrew White

    Andrew White March 19th, 2011 @ 06:36 AM

    Shouldn't it be in the global namespace? i.e. HomeController and not MyApp::HomeController.

  • Kevin Watt

    Kevin Watt March 19th, 2011 @ 02:43 PM

    That fixed it, thanks. When rendering your action you can't re-raise and expect that to get caught like normal, since it's not being wrapped in the begin block that does the rescue_from handling in the first place.

    I wish there was a way to have my web server (litespeed, but shouldn't matter) to see that it got a status 404 from the backend handler, then render the standard error page.

    Though routing errors still cause a 500 rather than 404 which sucks.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href=""></a>