{"record":{"id":"77da274b29747740","repo":"puppetlabs/puppet","slug":"could-not-find-a-valid-module-at-path","errorCode":null,"errorMessage":"Could not find a valid module at %{path}","messagePattern":"Could not find a valid module at %(.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/face/module/changes.rb","lineNumber":29,"sourceCode":"    EOT\n\n    returns _(\"Array of strings representing paths of modified files.\")\n\n    examples <<-EOT\n      Show modified files of an installed module:\n\n      $ puppet module changes /etc/puppetlabs/code/modules/vcsrepo/\n      warning: 1 files modified\n      lib/puppet/provider/vcsrepo.rb\n    EOT\n\n    arguments _(\"<path>\")\n\n    when_invoked do |path, options|\n      Puppet::ModuleTool.set_option_defaults options\n      root_path = Puppet::ModuleTool.find_module_root(path)\n      unless root_path\n        raise ArgumentError, _(\"Could not find a valid module at %{path}\") % { path: path.inspect }\n      end\n\n      Puppet::ModuleTool::Applications::Checksummer.run(root_path, options)\n    end\n\n    when_rendering :console do |return_value|\n      if return_value.empty?\n        Puppet.notice _(\"No modified files\")\n      else\n        Puppet.warning _(\"%{count} files modified\") % { count: return_value.size }\n      end\n      return_value.map(&:to_s).join(\"\\n\")\n    end\n  end\nend\n","sourceCodeStart":11,"sourceCodeEnd":45,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/face/module/changes.rb#L11-L45","documentation":"`puppet module changes <path>` (the `changes` action of the module Face) raises ArgumentError when Puppet::ModuleTool.find_module_root(path) returns nil. find_module_root ascends from the given path looking for a directory containing metadata.json; if none exists at or above the path, no module can be located and the tool refuses to check it for modified files.","triggerScenarios":"`puppet module changes /some/dir` where no metadata.json exists at or above that directory; passing a path to a single .pp file or a typo'd/nonexistent directory; running the command from the environment root instead of inside a module.","commonSituations":"Running from the wrong working directory; modules created before metadata.json was standard; control-repo role/profile directories that are not packaged modules; path typos.","solutions":["cd into the module root (the directory containing metadata.json) and run `puppet module changes .`","Verify a metadata.json exists: `ls <path>/metadata.json`; if missing, scaffold one with `puppet module generate` or copy from a module template","Double-check the path for typos and confirm it points at a directory, not a single file"],"exampleFix":"# before\npuppet module changes ~/repos/controlrepo/site/roles\n# after\ncd ~/repos/controlrepo/site/roles/ntp && puppet module changes .\n# (only works once metadata.json exists in that module root)","handlingStrategy":"validation","validationCode":"require 'pathname'\ndef module_root?(path)\n  Pathname.new(File.expand_path(path)).ascend.any? { |d| File.file?(File.join(d, 'metadata.json')) }\nend\nabort 'not a module: no metadata.json at or above this path' unless module_root?(ARGV[0] || Dir.pwd)","typeGuard":null,"tryCatchPattern":"begin\n  Puppet::Face[:module, :current].changes(path)\nrescue ArgumentError => e\n  abort \"#{e.message} — run from the directory containing metadata.json\"\nend","preventionTips":["Always run module-tool commands from the module root (where metadata.json lives)","Include a metadata.json check in module scaffolding CI so unpackaged directories fail early"],"tags":["puppet","module-tool","metadata","path-validation"],"backgroundTag":"module-metadata-missing","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}