It took me [insert large number here] years of Rails and Ruby but I finally saw this in my logs:
But the parameters were fine! It was just an innocuous XML document coming in through the API! After some flailing - basically bisecting a request payload - I reproduced it with:
To cut to the chase, adding a content type header solves it. With text/xml
, we get a garden-variety 404 error since there's no route for a POST
to /
:
So the root cause is that if there's no explicit content type on a request, Rack attempts to parse it as if it were application/x-www-form-urlencoded
and %
is invalid input in that case.
There are a couple of interesting elements to this one. As far as the exception is concerned, most blog posts and Stack Overflow questions around this error involve users mistyping URLs and putting in consecutive percent signs or something. So those are GET
requests, not POST
s, and thus not immediately relevant. Also, the exception comes from Rack. So the logs won't have a stracktrace and the usual error reporting tools won't get a chance to show a good post-mortem for this.
Poking around in Rack gets us moving though. Here's a comment from lib/rack/request.rb
:
So there's a reference to x-www-form-urlencoded, which refers to RFC 1738 for encoding, which explains why a percent sign, or a string like 15% of net
would be invalid input.
Supposing you can't add a content-type header to the code that's making the requests? Perhaps the POST
is coming from a third party. In that case, Rack middleware to the rescue. Nothing fancy, just:
This scopes the header addition to a specific URL space, which seems prudent. That's about it, hope this saves someone a few minutes!