UpgradeRuby.com Logo

The go-to resource for upgrading Ruby, Rails, and your dependencies.

Fixing ActionController::RespondToMismatchError in Rails


In the early days of Rails — before version 5.0 — we had a problem with content negotiation. When a controller action used respond_to to declare specific formats but received an unsupported one, Rails typically defaulted to HTML. This worked for web browsers, though it masked mismatches that could confuse API clients or bots crawling our endpoints.

Rails 5.0 changed that by raising ActionController::RespondToMismatchError explicitly.1 Now, when your action receives a request format it doesn’t handle, we see the issue clearly – before it causes silent failures down the line.

Of course, this error serves a purpose. It helps us build more maintainable applications over time. Before we examine triggers and fixes, let’s see how Rails negotiates formats and why we handle them explicitly in long-term projects.

Understanding the Error

Rails negotiates formats through the Accept header primarily, falling back to URL extensions like .json or parameters. Before respond_to executes, Rails matches the request against declared formats in the block. A mismatch raises the error early.

You might wonder what happens when a request asks for something outside the list, say via an Accept: application/xml header. Rails raises ActionController::RespondToMismatchError.2 Rather than defaulting silently – as it did pre-5.0 – this forces deliberate handling, which pays dividends in production over years.

Let’s look at a typical example. Suppose we have this controller action:

# app/controllers/articles_controller.rb
def show
  @article = Article.find(params[:id])

  respond_to do |format|
    format.html
    format.json { render json: @article }
  end
end

Notice how it supports only HTML and JSON. Let’s trigger a mismatch with curl. First, ensure your server runs and an Article with id 1 exists.

$ curl -H “Accept: application/xml” http://localhost:3000/articles/1

You see:

ActionController::RespondToMismatchError (ActionController::RespondToMismatchError)
The request for format 'xml' could not be fulfilled:
  app/controllers/articles_controller.rb:57:in `show'

The stack trace points to the respond_to block. $ curl -H “Accept: application/xml” http://localhost:3000/articles/1


Rails raises:

ActionController::RespondToMismatchError (ActionController::RespondToMismatchError: The request for format ‘xml’ could not be fulfilled):


Before 5.0, this often defaulted to HTML, though that hid real mismatches from API clients. Now we address them upfront - which matters more as our apps grow and attract diverse clients.{[Ruby on Rails Core Team. "Action Controller Overview." *Ruby on Rails Guides*. Accessed March 25, 2026. https://guides.rubyonrails.org/action_controller_overview.html#actioncontroller-respondtomismatcherror.]}

## Common Triggers

Before we examine fixes, let's catalog scenarios where you typically encounter this error. Each highlights aspects of your application's exposure to the wider world.

Users sometimes append formats manually:

$ curl http://localhost:3000/articles/1.pdf


Rails raises the same mismatch error. No action code beyond format check runs.

Frontend code might request the wrong format:

```javascript
fetch('/articles/1', {
  headers: { 'Accept': 'application/xml' }
})

Third-party clients using outdated expectations hit deprecated formats like /api/v1/users/123.xml.

Bots and scanners probe with formats like /admin/dashboard.json or /users/1.csv to test for leaks. These common patterns remind us that our endpoints face more than just browsers.

Resolution Approaches

Of course, we have several ways to address these mismatches. We’ll examine four common approaches here, though others exist — like middleware filters, which we won’t cover in depth. For each, note the trade-offs: when it shines, and where it falls short for long-term maintenance.

Add Support for the Format

If the request represents a legitimate need — say, clients expect PDF exports — extend your respond_to block:

def show
  @article = Article.find(params[:id])

  respond_to do |format|
    format.html
    format.json { render json: @article }
    format.pdf do
      pdf = generate_pdf(@article) # Your PDF logic, e.g., via wicked_pdf gem
      send_data pdf.render, filename: "#{@article.title}.pdf", type: 'application/pdf'
    end
  end
end

Gems like wicked_pdf or prawn handle PDFs. For CSV, @article.to_csv works well. This meets legitimate needs precisely. Though, each additional format adds maintenance – we must keep renderers updated and secure over time.3

Rescue at the Controller Level

Catch the error globally for clean production handling:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from ActionController::RespondToMismatchError, with: :handle_format_mismatch

  private

  def handle_format_mismatch(exception)
    logger.warn "Format mismatch: #{request.format} for #{request.path} (#{request.user_agent})"

    respond_to do |format|
      format.html { render file: "#{Rails.root}/public/406.html", status: :not_acceptable, layout: false }
      format.any  { head :not_acceptable }
    end
  end
end

This returns HTTP 406 Not Acceptable – semantically correct per RFC 7231.4

Note that your action code — database queries, say — runs before rescue_from catches the error. For expensive operations, this wastes resources. Logs let us track patterns, though high bot traffic can generate noise.

Approach 3: Use Routing Constraints

Restrict acceptable formats at the routing level to prevent invalid requests from reaching your controller:

# config/routes.rb
resources :articles, constraints: { format: /(html|json)/ }

# Or for a single route
get 'articles/:id', to: 'articles#show', constraints: { format: /html|json/ }

Unsupported formats return 404 Not Found before hitting the controller — no wasted computation. However, 404 signals “resource missing” rather than “format unsupported,” which might mislead API clients expecting 406.5

Approach 4: Remove respond_to Entirely

If your action only needs one format, eliminate the respond_to block:

def show
  @article = Article.find(params[:id])
  # No respond_to block needed for HTML-only actions
end

Rails defaults to the HTML template — no error for browsers. This keeps controllers lean. Non-HTML requests still fail, though, unless handled explicitly.

For API-only endpoints, render directly:

def show
  @article = Article.find(params[:id])
  render json: @article, status: :ok
end

No respond_to means no mismatch risk, though you lose format negotiation flexibility.

Choosing the Right Approach

Consider your client mix:

  • Public web apps with browsers and occasional bots: Rescue with 406 (Approach 2).
  • Strict APIs: Routing constraints (Approach 3) for early rejection.
  • HTML-only pages: Omit respond_to (Approach 4).
  • Verified new formats: Add support (Approach 1).6

Common Pitfalls

Rescue_from runs after action code. Heavy queries waste cycles. Use routes for performance-critical paths to reject early.

Bot traffic fills logs. Filter by user_agent or rate-limit at the edge.

Tests miss formats often. Test mismatches explicitly:

test "rejects XML" do
  get article_url(@article, format: :xml)
  assert_response :not_acceptable
end

Pre-5.0 Rails defaults silently. Post-7.0 negotiation grows stricter with better MIME parsing.

Debugging Tips

Check Your Routes

Verify which formats your routes accept:

rails routes | grep articles
rails routes -c ArticlesController

Look for format constraints in the output.

Examine Request Headers

In development, check the Rails logs for the actual request format:

Started GET "/articles/1.xml" for 127.0.0.1 at 2026-03-25 10:23:45 -0500
Processing by ArticlesController#show as XML

The as XML portion shows what format Rails detected.

Test Format Handling

Write controller tests for each supported format:

# test/controllers/articles_controller_test.rb
test "show as JSON" do
  article = articles(:one)
  get article_url(article, format: :json)
  assert_response :success
end

test "show as XML returns not acceptable" do
  article = articles(:one)
  assert_raises ActionController::RespondToMismatchError do
    get article_url(article, format: :xml)
  end
end

This ensures your format handling behaves as expected.

Monitor Production Errors

Log unsupported formats to identify patterns over time:

rescue_from ActionController::RespondToMismatchError do |exception|
  Rails.logger.warn "Unsupported format '#{request.format}' for #{request.path} from #{request.user_agent}"
  Sentry.capture_exception(exception, extra: { format: request.format.symbol, path: request.path })
  
  respond_to do |format|
    format.html { render file: "#{Rails.root}/public/406.html", status: :not_acceptable, layout: false }
    format.any { head :not_acceptable }
  end
end

Aggregate these logs quarterly. Do patterns suggest legitimate new formats, or just noise? This balances client evolution against maintenance costs over years.

Long-Term Maintenance

Review logs quarterly for format trends. Legitimate new clients may warrant support. Otherwise, tighten constraints via routes. This balances evolution against maintenance as apps age. Consider alternatives like content-negotiation middleware if needs grow complex.

Conclusion

ActionController::RespondToMismatchError surfaces mismatches early. We choose handling based on context and trade-offs for sustainable apps over years.

Citations

  1. Ruby on Rails Core Team. “5.0 Release Notes: ActionController::RespondToMismatchError.” . Accessed March 25, 2026. https://guides.rubyonrails.org/5_0_release_notes.html#actioncontroller-respondtomismatcherror.
  2. Ruby on Rails Core Team. “Action Controller Overview.” . Accessed March 25, 2026. https://guides.rubyonrails.org/action_controller_overview.html#method-i-respond_to.
  3. Ruby on Rails Core Team. “Action Controller Overview.” . Accessed March 25, 2026. https://guides.rubyonrails.org/action_controller_overview.html#method-i-render.
  4. Fielding, R. T., et al. “Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content.” . Internet Engineering Task Force (IETF), June 2014. https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.7.
  5. Ruby on Rails Core Team. “Rails Routing from the Outside In.” . Accessed March 25, 2026. https://guides.rubyonrails.org/routing.html#specifying-format-in-routes.
  6. Ruby on Rails Core Team. “Action Controller Overview.” . Accessed March 25, 2026. https://guides.rubyonrails.org/action_controller_overview.html#rendering-by-content-type.

You May Also Like