Rollbar で Rack::QueryParser::InvalidParameterError が通知されるのを抑止する
Rails のエラーを捕捉するのに Rollbar を使っている場合、次のような通知を受け取ることがあります。
Rack::QueryParser::InvalidParameterError: invalid byte sequence in UTF-8
ArgumentError: invalid byte sequence in UTF-8
+ 89 non-project frames
Rack::QueryParser::InvalidParameterError: invalid byte sequence in UTF-8
+ 87 non-project frames
ArgumentError: invalid byte sequence in UTF-8
+ 85 non-project frames
Rack::QueryParser::InvalidParameterError: invalid byte sequence in UTF-8
+ 83 non-project frames
大抵の場合、このエラーは middleware レイヤーで起きるエラーであり、Rails では Rack::QueryParser::InvalidParameterError
(= Rack::Utils::InvalidParameterError
) を rescue して 400 を返すようになっています。
cf. https://github.com/rails/rails/commit/d59a24d543b4fd34d453e8209caae5fef315ea78
よって、特別な事情がない限りは config/initializers/rollbar.rb に次の 1 行を追加すると良いです。
config.exception_level_filters.merge!('Rack::QueryParser::InvalidParameterError' => 'ignore')
本題は以上です。以下、調べたことについてつらつら。
Rack::QueryParser::InvalidParameterError の歴史
Rack は application/x-www-form-urlencoded のパラメータのキーに UTF-8 として不正なシーケンスが存在すると、params
を参照した時に例外を投げるという問題が随分前からあります。
cf. ArgumentError: invalid byte sequence in UTF-8 · Issue #673 · rack/rack
次のようなログを見たことがある人も多いのではないでしょうか。
Rack::QueryParser::InvalidParameterError (invalid byte sequence in UTF-8):
rack (2.0.5) lib/rack/query_parser.rb:72:in `rescue in parse_nested_query'
rack (2.0.5) lib/rack/query_parser.rb:60:in `parse_nested_query'
rack (2.0.5) lib/rack/request.rb:468:in `parse_query'
rack (2.0.5) lib/rack/request.rb:343:in `POST'
actionpack (5.2.1) lib/action_dispatch/http/request.rb:382:in `block (2 levels) in POST'
actionpack (5.2.1) lib/action_dispatch/http/parameters.rb:109:in `block in parse_formatted_parameters'
actionpack (5.2.1) lib/action_dispatch/http/parameters.rb:109:in `fetch'
actionpack (5.2.1) lib/action_dispatch/http/parameters.rb:109:in `parse_formatted_parameters'
actionpack (5.2.1) lib/action_dispatch/http/request.rb:381:in `block in POST'
rack (2.0.5) lib/rack/request.rb:57:in `fetch'
rack (2.0.5) lib/rack/request.rb:57:in `fetch_header'
actionpack (5.2.1) lib/action_dispatch/http/request.rb:380:in `POST'
actionpack (5.2.1) lib/action_dispatch/http/parameters.rb:55:in `parameters'
actionpack (5.2.1) lib/action_dispatch/http/filter_parameters.rb:43:in `filtered_parameters'
actionpack (5.2.1) lib/action_controller/metal/instrumentation.rb:23:in `process_action'
actionpack (5.2.1) lib/action_controller/metal/params_wrapper.rb:256:in `process_action'
activerecord (5.2.1) lib/active_record/railties/controller_runtime.rb:24:in `process_action'
actionpack (5.2.1) lib/abstract_controller/base.rb:134:in `process'
actionview (5.2.1) lib/action_view/rendering.rb:32:in `process'
(snip)
似たような問題として、不正なパーセントエンコーディングのパラメータが送られてくると例外を投げるという問題もあります。
cf. invalid %-encoding error in application for malformed uri · Issue #337 · rack/rack
いずれも「そのようなリクエストに対処するのは Rack の責務じゃない」というスタンスを貫いているので、Rack 側で何らかの対応が入ることはないでしょう。
そこで導入されたのが Rack::Utils::InvalidParameterError
です。
cf. Raise specific exception if the parameters are invalid by rafaelfranca · Pull Request #713 · rack/rack
Rails では 4.2 からこのエラーを rescue して 400 を返すようになりました。
cf. https://github.com/rails/rails/commit/d59a24d543b4fd34d453e8209caae5fef315ea78
なお、Rack::QueryParser::InvalidParameterError
と Rack::Utils::InvalidParameterError
は同じものです。
最終的に rescue されるエラーがどうして Rollbar に捕捉されるのか?
Rails 5.2 の場合、middleware stack は次のようになっています。
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run MyApp::Application.routes
cf. https://guides.rubyonrails.org/rails_on_rack.html#inspecting-middleware-stack
これに対し、Rollbar は ActionDispatch::DebugExceptions
の後に Rollbar::Middleware::Rails::RollbarMiddleware
を挿入します。
よって、ActionDispatch::DebugExceptions
より後に起きた例外を捕捉して通知できるようになるわけです。
cf. https://github.com/rollbar/rollbar-gem/blob/v2.8.0/lib/rollbar/middleware/rails/rollbar.rb#L31-L34
Rack::Utils::InvalidParameterError
を rescue しているのは ActiveSupport::Cache::Strategy::LocalCache::Middleware
で、Rollbar::Middleware::Rails::RollbarMiddleware
より前の middleware なので、最終的に rescue されるエラーも通知されることになります。