{"record":{"id":"bfdfa3262c6b2c6c","repo":"puppetlabs/puppet","slug":"could-not-action-module-name-module-has-h-bfdfa3","errorCode":null,"errorMessage":"Could not %{action} '%{module_name}'; module has had changes made locally","messagePattern":"Could not %(.+?) '%(.+?)'; module has had changes made locally","errorType":"exception","errorClass":"Puppet::ModuleTool::Errors::LocalChangesError","httpStatus":null,"severity":"error","filePath":"lib/puppet/module_tool/applications/upgrader.rb","lineNumber":85,"sourceCode":"          # (or something similar).\n          def installed_release.priority\n            0\n          end\n\n          mod = installed_release.mod\n          results[:installed_version] = SemanticPuppet::Version.parse(mod.version)\n          dir = Pathname.new(mod.modulepath)\n\n          vstring = mod.version ? \"v#{mod.version}\" : '???'\n          Puppet.notice _(\"Found '%{name}' (%{version}) in %{dir} ...\") % { name: name, version: colorize(:cyan, vstring), dir: dir }\n          unless @ignore_changes\n            changes = begin\n              Checksummer.run(mod.path)\n            rescue\n              []\n            end\n            if mod.has_metadata? && !changes.empty?\n              raise LocalChangesError,\n                    :action => :upgrade,\n                    :module_name => name,\n                    :requested_version => results[:requested_version],\n                    :installed_version => mod.version\n            end\n          end\n\n          Puppet::Forge::Cache.clean\n\n          # Ensure that there is at least one candidate release available\n          # for the target package.\n          available_versions = module_repository.fetch(name)\n          if available_versions.empty?\n            raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host)\n          elsif results[:requested_version] != :latest\n            requested = Puppet::Module.parse_range(results[:requested_version])\n            unless available_versions.any? { |m| requested.include? m.version }\n              raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host)","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/module_tool/applications/upgrader.rb#L67-L103","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before\n$ puppet module upgrade puppetlabs-stdlib\n# Error: Could not upgrade 'puppetlabs-stdlib'; module has had changes made locally\n\n# after\n$ puppet module upgrade puppetlabs-stdlib --ignore-changes","handlingStrategy":"validation","validationCode":"mod = env.modules.find { |m| m.forge_name && m.forge_name.tr('/', '-') == name }\nif mod && mod.has_metadata? && !options[:force] && !options[:ignore_changes]\n  changes = begin\n    Puppet::ModuleTool::Applications::Checksummer.run(mod.path)\n  rescue StandardError\n    []\n  end\n  abort 'local edits present — commit them to a fork or pass --ignore-changes' unless changes.empty?\nend","typeGuard":null,"tryCatchPattern":"result = Puppet::ModuleTool::Applications::Upgrader.new(name, options).run\nif result[:error] && result[:error][:oneline] =~ /changes made locally/\n  result = Puppet::ModuleTool::Applications::Upgrader\n    .new(name, options.merge(ignore_changes: true)).run\nend","preventionTips":["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"],"tags":["puppet","module-tool","upgrade","checksum","local-changes"],"backgroundTag":"local-modifications-detected","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}