puppetlabs/puppet · error · RuntimeError

Loading of #{name} using relative path: '#{loaded_path}' did

Error message

Loading of #{name} using relative path: '#{loaded_path}' did not create expected class

What it means

When ClassLoader resolves a dotted name to a Ruby constant it first tries Object's namespace chain, then the Puppet autoloader. This RuntimeError means the autoloader reported a file as loaded, but the expected constant still does not exist after the load — the file on disk does not match the name being resolved.

Source

Thrown at lib/puppet/pops/types/class_loader.rb:96

  def self.provide_from_string(name)
    name_path = name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)
    # always from the root, so remove an empty first segment
    name_path.shift if name_path[0].empty?
    provide_from_name_path(name, name_path)
  end

  def self.provide_from_name_path(name, name_path)
    # If class is already loaded, try this first
    result = find_class(name_path)

    unless result.is_a?(Module)
      # Attempt to load it using the auto loader
      loaded_path = nil
      if paths_for_name(name_path).find { |path| loaded_path = path; @autoloader.load(path, Puppet.lookup(:current_environment)) }
        result = find_class(name_path)
        unless result.is_a?(Module)
          raise RuntimeError, "Loading of #{name} using relative path: '#{loaded_path}' did not create expected class"
        end
      end
    end
    return nil unless result.is_a?(Module)

    result
  end
  private_class_method :provide_from_string

  def self.find_class(name_path)
    name_path.reduce(Object) do |ns, name|
      ns.const_get(name, false) # don't search ancestors
    rescue NameError
      return nil
    end
  end
  private_class_method :find_class

View on GitHub (pinned to e227c27540)

Solutions

  1. Make the file's constant nesting match the resolved name path exactly (module PuppetX; module MyModule; class Thing ... for PuppetX::MyModule::Thing).
  2. Check character casing of both the file name and every module/class segment against the requested name.
  3. Ensure the implementation file itself defines (or reopens) the constant — requiring another file that defines it elsewhere in the path is not enough if the namespace differs.
  4. Reproduce in a Ruby shell: require the file manually, then check Object.const_defined?('PuppetX::MyModule::Thing') to see what was actually defined.

Example fix

# before — lib/puppet_x/my_module/thing.rb
class Thing; end   # defines ::Thing, not PuppetX::MyModule::Thing

# after
module PuppetX
  module MyModule
    class Thing; end
  end
end
Defensive patterns

Strategy: validation

Validate before calling

# Ruby — verify the constant exists after an explicit require, before relying on ClassLoader
path = 'puppet_x/mymod/thing'
require path
unless Object.const_defined?('PuppetX::MyMod::Thing', false)
  raise LoadError, "#{path} loaded but PuppetX::MyMod::Thing is not defined — check nesting/casing"
end

Type guard

def resolves_to_module?(name_path)
  name_path.reduce(Object) do |ns, n|
    return false unless ns.const_defined?(n, false)
    ns.const_get(n, false)
  end.is_a?(Module)
end

Try / catch

begin
  Puppet::Pops::Types::ClassLoader.provide(name)
rescue RuntimeError => e
  # report file vs constant mismatch instead of failing opaquely
  raise LoadError, "#{e.message} — verify the file defines #{name} with exact nesting and casing"
end

Prevention

When it happens

Trigger: ClassLoader.provide / provide_from_name_path resolving a name whose autoload file exists (e.g. under lib/puppet_x/<module>/ or lib/puppet/...) and executes cleanly, but defines the constant in a different namespace, with different casing, or not at all (e.g. it only requires another file).

Common situations: File name vs constant casing mismatch that only fails on case-sensitive filesystems (Linux CI after passing on macOS/Windows); module renamed internally but not the file layout; an implementation file that delegates to another file instead of defining/reopening the constant; environment load-path changes that pick up a stale file.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/d1d42d771c736454. Report an issue: GitHub.