A gem we were building requires only the one ActiveSupport file it needs, rather than pulling in all of Rails. On Rails 7.1 that raised:

NoMethodError: undefined method 'deprecator' for module ActiveSupport
activesupport-7.1.x/lib/active_support/core_ext/time/conversions.rb:62:in `<class:Time>'

triggered by:

require "active_support/encrypted_configuration"

The bug was requiring a leaf file without requiring its parent.

Why it happens

"Require only what you need" is the normal advice for a gem that doesn't want to load all of Rails just to use one class. So the gem required exactly the ActiveSupport file it cared about and nothing else, and every test using that file passed under Rails 7.2 and 8.0. Under 7.1, the same require blew up before the file even finished loading.

Rails 7.1 introduced per-framework deprecators. ActiveSupport's own core_ext files now reference ActiveSupport.deprecator directly, and that accessor only gets set up when active_support itself, the parent library, has been loaded. Requiring a leaf file pulls in its core_ext dependencies transitively, but never triggers that parent initialization. The ActiveSupport constant exists. The method on it doesn't.

The fix

One line, at the top of every file that requires an ActiveSupport file directly:

require "active_support"
require "active_support/encrypted_configuration"

Takeaway

"Require only what you need" is good advice that stops being true at ActiveSupport's boundary. Require active_support first, always, before any active_support/... leaf file. And if your gem wraps framework internals, test it against a matrix of framework versions rather than one pinned Rails release. See testing a gem across every adapter combination for the Appraisal side of that.