Summary
The json gem 3.0.0 was released on 2026-09-07. CI on every PR opened since then fails with about 1590 spec failures, for example the ruby 3.4 job on #653: https://github.com/OpenVoxProject/openvox/actions/runs/34143447163/job/101810265246. main passed the day before on the same base commit with json 2.x.
Why CI picks it up
Gemfile.lock is not tracked, and gem 'json' in the :packaging group of the Gemfile has no version constraint. The CI bundler-cache: true step installs all non-optional groups, so it resolves the newest json from rubygems, and that overrides the json default gem that ships with Ruby inside the test process. Dependabot is configured for bundler but can only bump constraints that exist in the Gemfile, so an unconstrained gem never gets a PR.
json is not a gemspec runtime dependency. Real installs use the json bundled with their Ruby, so this currently only affects CI. It will affect real installs once a Ruby release ships json 3 as its default gem.
What breaks
json 3.0.0 makes every option a keyword argument and removes several APIs. Three call sites in lib/puppet/util/json.rb are affected:
Puppet::Util::Json.load passes an options hash positionally to ::JSON.parse(string, options). This now raises ArgumentError: wrong number of arguments (given 2, expected 1). Module metadata, catalogs, facts, and file metadata all go through this, so most of the failures cascade from it (994 of them are undefined method 'fetch' for nil from metadata loads returning nil).
Puppet::Util::Json.dump references ::JSON::PRETTY_STATE_PROTOTYPE, which was removed. This surfaces as NameError: uninitialized constant JSON::PRETTY_STATE_PROTOTYPE from puppet facts show.
- Two specs call
JSON.pretty_unparse, which was removed.
Also relevant from the changelog: allow_duplicate_key now defaults to false for both parsing and generating, and unknown options raise instead of being ignored. Both may surface once the above is fixed.
Proposed fix
Short term, to unblock CI and give Dependabot something to bump:
in the :packaging group of the Gemfile.
Then port Puppet::Util::Json to the json 3 API in a way that still works with the json 2.x that ships with Ruby 3.2 through 4.0: pass options as keyword arguments to JSON.parse, and replace the PRETTY_STATE_PROTOTYPE merge with JSON.pretty_generate or the equivalent state options. Check the allow_duplicate_key default against any fixtures or wire formats that rely on the old behaviour.
Upstream puppetlabs/puppet has nothing on this yet.
Summary
The json gem 3.0.0 was released on 2026-09-07. CI on every PR opened since then fails with about 1590 spec failures, for example the ruby 3.4 job on #653: https://github.com/OpenVoxProject/openvox/actions/runs/34143447163/job/101810265246.
mainpassed the day before on the same base commit with json 2.x.Why CI picks it up
Gemfile.lockis not tracked, andgem 'json'in the:packaginggroup of theGemfilehas no version constraint. The CIbundler-cache: truestep installs all non-optional groups, so it resolves the newest json from rubygems, and that overrides the json default gem that ships with Ruby inside the test process. Dependabot is configured for bundler but can only bump constraints that exist in theGemfile, so an unconstrained gem never gets a PR.jsonis not a gemspec runtime dependency. Real installs use the json bundled with their Ruby, so this currently only affects CI. It will affect real installs once a Ruby release ships json 3 as its default gem.What breaks
json 3.0.0 makes every option a keyword argument and removes several APIs. Three call sites in
lib/puppet/util/json.rbare affected:Puppet::Util::Json.loadpasses an options hash positionally to::JSON.parse(string, options). This now raisesArgumentError: wrong number of arguments (given 2, expected 1). Module metadata, catalogs, facts, and file metadata all go through this, so most of the failures cascade from it (994 of them areundefined method 'fetch' for nilfrom metadata loads returning nil).Puppet::Util::Json.dumpreferences::JSON::PRETTY_STATE_PROTOTYPE, which was removed. This surfaces asNameError: uninitialized constant JSON::PRETTY_STATE_PROTOTYPEfrompuppet facts show.JSON.pretty_unparse, which was removed.Also relevant from the changelog:
allow_duplicate_keynow defaults tofalsefor both parsing and generating, and unknown options raise instead of being ignored. Both may surface once the above is fixed.Proposed fix
Short term, to unblock CI and give Dependabot something to bump:
in the
:packaginggroup of theGemfile.Then port
Puppet::Util::Jsonto the json 3 API in a way that still works with the json 2.x that ships with Ruby 3.2 through 4.0: pass options as keyword arguments toJSON.parse, and replace thePRETTY_STATE_PROTOTYPEmerge withJSON.pretty_generateor the equivalent state options. Check theallow_duplicate_keydefault against any fixtures or wire formats that rely on the old behaviour.Upstream puppetlabs/puppet has nothing on this yet.