puppetlabs/puppet · error · Puppet::ModuleTool::Errors::InvalidModuleError
Could not %{action} '%{module_name}'; %{error}
Error message
Could not %{action} '%{module_name}'; %{error} What it means
Raised when Puppet::ModuleTool::LocalTarball (a SemanticPuppet dependency source that reads a local .tar.gz module package) fails while loading the tarball and raises a Puppet::Module::Error; the installer rescues it in `local_tarball_source` and re-raises it as InvalidModuleError (lib/puppet/module_tool/errors/shared.rb:212). The nested %{error} is the original message, almost always a failure to find or parse metadata.json inside the tarball. It means the file you passed looks like a module package but its metadata is missing, malformed, or not valid JSON with the required fields.
Source
Thrown at lib/puppet/module_tool/applications/installer.rb:258
}
ensure
results[:result] ||= :failure
end
results
end
private
def module_repository
@repo ||= Puppet::Forge.new(Puppet[:module_repository])
end
def local_tarball_source
@tarball_source ||= begin
Puppet::ModuleTool::LocalTarball.new(@name)
rescue Puppet::Module::Error => e
raise InvalidModuleError.new(@name, :action => @action, :error => e)
end
end
def installed_modules_source
@installed ||= Puppet::ModuleTool::InstalledModules.new(@environment)
end
def installed_modules
installed_modules_source.modules
end
def build_single_module_graph(name, version)
range = Puppet::Module.parse_range(version)
graph = SemanticPuppet::Dependency::Graph.new(name => range)
releases = SemanticPuppet::Dependency.fetch_releases(name)
releases.each { |release| release.dependencies.clear }
graph << releases
endView on GitHub (pinned to e227c27540)
Solutions
- Build the package with `puppet module build ./my-module` and install the resulting ./pkg/*.tar.gz
- Inspect the tarball: `tar tzf foo.tar.gz | head` — metadata.json must sit directly under the module root directory
- Validate JSON: `ruby -rjson -e 'puts JSON.parse(File.read("metadata.json"))'` and fix syntax errors
- Ensure metadata.json contains required fields (name, version, source) and the name matches owner-module with a hyphen
Example fix
# before # tarball built by zipping the parent directory, metadata.json buried one level too deep puppet module install ./foo.tar.gz # after cd my-module && puppet module build . puppet module install ./pkg/my-module-0.1.0.tar.gz
Defensive patterns
Strategy: try-catch
Validate before calling
# Verify a tarball is a well-formed module package before installing
require 'json'
require 'rubygems/package'
Gem::Package::TarReader.new(Zlib::GzipReader.open('foo.tar.gz')) do |tar|
entry = tar.find { |t| t.full_name.end_with?('metadata.json') }
raise 'no metadata.json at module root' unless entry
JSON.parse(entry.read)
end Try / catch
begin
Puppet::ModuleTool::Applications::Installer.run('./foo.tar.gz', options)
rescue Puppet::ModuleTool::Errors::InvalidModuleError => e
# e.message embeds the underlying Puppet::Module::Error (usually a metadata.json parse failure)
puts e.multiline
raise
end Prevention
- Produce packages with `puppet module build`, never ad-hoc tar/czip of the source tree
- After editing metadata.json, run a JSON parse check before packaging
- Confirm `tar tzf pkg.tar.gz` shows metadata.json directly under the module directory
When it happens
Trigger: `puppet module install ./foo.tar.gz` where the archive lacks metadata.json, has a syntax error in it (trailing commas, comments), or metadata.json is at the wrong nesting level (not at the root of the module directory inside the tarball). In code: Installer.run('./foo.tar.gz', ...) where Puppet::ModuleTool::LocalTarball.new raises Puppet::Module::Error during unpack/parse.
Common situations: Hand-rolled tarballs built with `tar czf` from the wrong directory (metadata.json ends up under an extra path component); editing metadata.json and introducing invalid JSON; tarring the module's parent folder instead of the module folder; a truncated download or a GitHub 'Download ZIP' artifact used in place of a `puppet module build` package.
Related errors
- Could not %{action} '%{module_name}', did you mean '%{sugges
- Unable to find metadata.json in module root at %{path} See h
- Could not parse filename to obtain the username, module name
- Could not find a valid module at %{path}
- Could not find %{path} on disk
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/7553f0ecc3e8c567.
Report an issue: GitHub.