puppetlabs/puppet · error · Puppet::ModuleTool::Errors::LocalChangesError
Could not %{action} '%{module_name}'; module has had changes
Error message
Could not %{action} '%{module_name}'; module has had changes made locally What it means
The upgrade-path twin of the uninstall LocalChangesError: Puppet::ModuleTool::Errors::LocalChangesError raised by the Upgrader when the installed module's files differ from its release checksums. Before replacing a module, Puppet wants confirmation you will not lose edits; @ignore_changes is options[:force] || options[:ignore_changes], so either CLI flag suppresses the check. Note the bare `rescue` around Checksummer.run means modules without parseable checksums are treated as unchanged.
Source
Thrown at lib/puppet/module_tool/applications/upgrader.rb:85
# (or something similar).
def installed_release.priority
0
end
mod = installed_release.mod
results[:installed_version] = SemanticPuppet::Version.parse(mod.version)
dir = Pathname.new(mod.modulepath)
vstring = mod.version ? "v#{mod.version}" : '???'
Puppet.notice _("Found '%{name}' (%{version}) in %{dir} ...") % { name: name, version: colorize(:cyan, vstring), dir: dir }
unless @ignore_changes
changes = begin
Checksummer.run(mod.path)
rescue
[]
end
if mod.has_metadata? && !changes.empty?
raise LocalChangesError,
:action => :upgrade,
:module_name => name,
:requested_version => results[:requested_version],
:installed_version => mod.version
end
end
Puppet::Forge::Cache.clean
# Ensure that there is at least one candidate release available
# for the target package.
available_versions = module_repository.fetch(name)
if available_versions.empty?
raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host)
elsif results[:requested_version] != :latest
requested = Puppet::Module.parse_range(results[:requested_version])
unless available_versions.any? { |m| requested.include? m.version }
raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host)View on GitHub (pinned to e227c27540)
Solutions
- Re-run with `puppet module upgrade <name> --ignore-changes` to keep only the checksum check suppressed, or --force to also relax graph constraints
- Move your patches into a fork or a wrapper module so the Forge module stays pristine
- Check what changed via `puppet module changes <module-dir>` and revert unintended modifications, then upgrade normally
- Manage the environment with a Puppetfile + r10k so upgrades happen through version control instead of in place
Example fix
# before $ puppet module upgrade puppetlabs-stdlib # Error: Could not upgrade 'puppetlabs-stdlib'; module has had changes made locally # after $ puppet module upgrade puppetlabs-stdlib --ignore-changes
Defensive patterns
Strategy: validation
Validate before calling
mod = env.modules.find { |m| m.forge_name && m.forge_name.tr('/', '-') == name }
if mod && mod.has_metadata? && !options[:force] && !options[:ignore_changes]
changes = begin
Puppet::ModuleTool::Applications::Checksummer.run(mod.path)
rescue StandardError
[]
end
abort 'local edits present — commit them to a fork or pass --ignore-changes' unless changes.empty?
end Try / catch
result = Puppet::ModuleTool::Applications::Upgrader.new(name, options).run
if result[:error] && result[:error][:oneline] =~ /changes made locally/
result = Puppet::ModuleTool::Applications::Upgrader
.new(name, options.merge(ignore_changes: true)).run
end Prevention
- Carry patches in a forked module released to your own Forge, not as live edits
- Standardize on --ignore-changes only after an explicit backup step in pipelines
- Use `puppet module changes` in CI as a drift detector so upgrades never surprise you
When it happens
Trigger: Running `puppet module upgrade <author-module>` without --force/-f or --ignore-changes/-c when the installed module has metadata and Checksummer.run(mod.path) reports differences (line 78-90 of upgrader.rb).
Common situations: Patching Forge modules in place instead of forking; editor/CRLF or permission churn altering digests; content modified by older tooling; CI pipelines that mutate module files before running upgrades.
Related errors
- Could not %{action} '%{module_name}'; module has had changes
- Could not %{action} '%{module_name}'; module is not installe
- Could not %{action} '%{module_name}'; module appears in mult
- Could not %{action} '%{module_name}'; no releases are availa
- Could not %{action} '%{module_name}'; no releases matching '
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/bfdfa3262c6b2c6c.
Report an issue: GitHub.