{"record":{"id":"4327ec97a6adde57","repo":"puppetlabs/puppet","slug":"could-not-action-module-name-module-has-h","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/uninstaller.rb","lineNumber":103,"sourceCode":"                  :module_name => @name\n          end\n        end\n      end\n\n      def validate_module\n        mod = @installed.first\n\n        unless @ignore_changes\n          raise _(\"Either the `--ignore_changes` or `--force` argument must be specified to uninstall modules when running in FIPS mode.\") if Puppet.runtime[:facter].value(:fips_enabled)\n\n          changes = begin\n            Puppet::ModuleTool::Applications::Checksummer.run(mod.path)\n          rescue ArgumentError\n            []\n          end\n\n          if mod.has_metadata? && !changes.empty?\n            raise LocalChangesError,\n                  :action => :uninstall,\n                  :module_name => (mod.forge_name || mod.name).tr('/', '-'),\n                  :requested_version => @options[:version],\n                  :installed_version => mod.version\n          end\n        end\n\n        if !@options[:force] && !mod.required_by.empty?\n          raise ModuleIsRequiredError,\n                :module_name => (mod.forge_name || mod.name).tr('/', '-'),\n                :required_by => mod.required_by,\n                :requested_version => @options[:version],\n                :installed_version => mod.version\n        end\n      end\n    end\n  end\nend","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/module_tool/applications/uninstaller.rb#L85-L121","documentation":"Raised by Puppet::ModuleTool::Applications::Uninstaller (class Puppet::ModuleTool::Errors::LocalChangesError) when you uninstall a module whose files no longer match the checksums recorded for the installed release. Before deleting, the uninstaller runs Puppet::ModuleTool::Applications::Checksummer.run(mod.path) and refuses to destroy locally-modified content unless changes are explicitly ignored. The guard is skipped entirely when --force or --ignore-changes is passed, since @ignore_changes = options[:force] || options[:ignore_changes].","triggerScenarios":"Running `puppet module uninstall <author-module>` (or Uninstaller.new(name, options).run) where the target module has a valid metadata.json (has_metadata? true), Checksummer.run(mod.path) returns a non-empty change set, and neither --force/-f nor --ignore-changes/-c is set.","commonSituations":"A module from the Forge was hotfixed in place to work around a bug; files were touched by an editor, CI, or a sync tool (line-ending/CRLF rewrites on Windows); an older r10k/librarian-puppet checkout left the tree dirty; permissions or ownership changes alter file digests.","solutions":["Re-run with `puppet module uninstall <name> --force` (or `--ignore-changes` if you only want to skip the checksum check but keep the dependents check)","If the edits matter, copy the module directory aside first (cp -r) and then uninstall with --force","Inspect what changed with `puppet module changes <module-dir>` and revert unintended edits, then uninstall without --force","Restore pristine content by reinstalling the same release (`puppet module install <name> --version <v> --force`) and then uninstall cleanly"],"exampleFix":"# before\n$ puppet module uninstall puppetlabs-stdlib\n# Error: Could not uninstall 'puppetlabs-stdlib'; module has had changes made locally\n\n# after\n$ puppet module uninstall puppetlabs-stdlib --force","handlingStrategy":"validation","validationCode":"require 'puppet/module_tool/applications/checksummer'\n\nmod = env.modules.find { |m| (m.forge_name || m.name).tr('/', '-') == name }\nif mod && mod.has_metadata?\n  changes = begin\n    Puppet::ModuleTool::Applications::Checksummer.run(mod.path)\n  rescue ArgumentError\n    []\n  end\n  abort \"refusing: #{changes.length} locally changed files\" unless changes.empty? || options[:force] || options[:ignore_changes]\nend","typeGuard":null,"tryCatchPattern":"result = Puppet::ModuleTool::Applications::Uninstaller.run(name, options)\nif result[:error] && result[:error][:oneline] =~ /changes made locally/\n  # decide: keep edits, back up, or retry with force: true\n  FileUtils.cp_r(mod.path, \"#{mod.path}.bak\")\n  retry_with = options.merge(force: true)\nend","preventionTips":["Never edit Forge modules in place — fork them or wrap them in profile modules","Manage module content with Puppetfile + r10k so uninstall/upgrade happens in version control, not on live trees","In automation, pass --force or --ignore-changes deliberately and log the Checksummer diff first","Run `puppet module changes <dir>` before scripted uninstall/upgrade to make drift visible"],"tags":["puppet","module-tool","uninstall","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"}