Rails 8.2 drops Marshal message fallback

Rails 8.2 changes config.active_support.message_serializer to :json for applications using config.load_defaults 8.2. This change is merged on Rails main; Rails 8.2 is not yet released.

The 7.1 through 8.1 defaults already write JSON, but accept older Marshal messages through :json_allow_marshal. The new default stops accepting Marshal when reading messages through ActiveSupport::MessageVerifier and ActiveSupport::MessageEncryptor. That can affect older Marshal-serialized tokens read through the application’s default message verifiers.

Before

We can see the compatibility behavior with an old Marshal message. Run the following two snippets sequentially with Active Support 8.1.4 or 8.2.0.alpha (tested at Rails main c4f04bd):

require "active_support"
require "active_support/message_verifier"

secret = "example-signing-secret" * 4
payload = { "report_id" => 42 }

legacy_writer = ActiveSupport::MessageVerifier.new(secret, serializer: :marshal)
legacy_message = legacy_writer.generate(payload)

compatible = ActiveSupport::MessageVerifier.new(secret, serializer: :json_allow_marshal)
p compatible.verified(legacy_message)

The result is { "report_id" => 42 }. The reader accepts Marshal even though it generates new messages using JSON. The secret above is only for this standalone example.

With the Rails 8.2 default

The :json serializer rejects that Marshal message while continuing to read existing JSON messages:

strict = ActiveSupport::MessageVerifier.new(secret, serializer: :json)

begin
  strict.verified(legacy_message)
rescue RuntimeError => error
  puts error.message
end

p strict.verified(compatible.generate(payload))

The first call raises RuntimeError with Unsupported serialization format, which the example catches. The JSON message still returns { "report_id" => 42 }. Both verified and verify raise for this unsupported format; verified does not return nil in this case. The serializer options already exist in earlier Rails versions; the new behavior is the 8.2 default. The :json serializer also retains the documented MessagePack fallback; it removes Marshal support rather than accepting only JSON.

Installing Rails 8.2 while retaining config.load_defaults 8.1 keeps the previous default. An explicitly configured serializer still takes precedence.

Check old messages before switching

While the application still uses :json_allow_marshal, add this subscription in an initializer:

ActiveSupport::Notifications.subscribe("message_serializer_fallback.active_support") do |event|
  if event.payload[:fallback] == :marshal
    Rails.logger.warn("Legacy Marshal message read")
  end
end

This records each Marshal fallback without logging the token or deserialized data. Exercise the affected paths and account for the lifetime of existing messages before removing the fallback. A quiet observation window does not prove that all old tokens have expired; retire or reissue messages that must remain usable.

We can adopt the stricter setting independently of the other framework defaults in config/application.rb:

config.active_support.message_serializer = :json

Audit custom verifiers and encryptors with explicit serializer options separately. This setting does not change the separate cookie or cache serializer configuration.

For the earlier serializer choices, see our Rails 7.1 MessagePack article. Ben Sheldon introduced the new default in Rails PR #59156. See the message serializer configuration guide for the supported formats and fallback table.

Need expert help with Rails?

Saeloun is a Rails Foundation Contributing Member helping teams modernize, upgrade, scale, and maintain production Rails applications.

Our Expertise

  • Rails contributors
  • 500+ Technical Articles
  • Production Rails consulting
  • Performance Optimization

Services

  • Rails application development
  • Code Audits
  • Rails upgrades
  • Team Augmentation

Need help on your Ruby on Rails or React project?

Join Our Newsletter