{"record":{"id":"d1d42d771c736454","repo":"puppetlabs/puppet","slug":"loading-of-name-using-relative-path-loaded","errorCode":null,"errorMessage":"Loading of #{name} using relative path: '#{loaded_path}' did not create expected class","messagePattern":"Loading of #(.+?) using relative path: '#(.+?)' did not create expected class","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/types/class_loader.rb","lineNumber":96,"sourceCode":"\n  def self.provide_from_string(name)\n    name_path = name.split(TypeFormatter::NAME_SEGMENT_SEPARATOR)\n    # always from the root, so remove an empty first segment\n    name_path.shift if name_path[0].empty?\n    provide_from_name_path(name, name_path)\n  end\n\n  def self.provide_from_name_path(name, name_path)\n    # If class is already loaded, try this first\n    result = find_class(name_path)\n\n    unless result.is_a?(Module)\n      # Attempt to load it using the auto loader\n      loaded_path = nil\n      if paths_for_name(name_path).find { |path| loaded_path = path; @autoloader.load(path, Puppet.lookup(:current_environment)) }\n        result = find_class(name_path)\n        unless result.is_a?(Module)\n          raise RuntimeError, \"Loading of #{name} using relative path: '#{loaded_path}' did not create expected class\"\n        end\n      end\n    end\n    return nil unless result.is_a?(Module)\n\n    result\n  end\n  private_class_method :provide_from_string\n\n  def self.find_class(name_path)\n    name_path.reduce(Object) do |ns, name|\n      ns.const_get(name, false) # don't search ancestors\n    rescue NameError\n      return nil\n    end\n  end\n  private_class_method :find_class\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/types/class_loader.rb#L78-L114","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Make the file's constant nesting match the resolved name path exactly (module PuppetX; module MyModule; class Thing ... for PuppetX::MyModule::Thing).","Check character casing of both the file name and every module/class segment against the requested name.","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.","Reproduce in a Ruby shell: require the file manually, then check Object.const_defined?('PuppetX::MyModule::Thing') to see what was actually defined."],"exampleFix":"# before — lib/puppet_x/my_module/thing.rb\nclass Thing; end   # defines ::Thing, not PuppetX::MyModule::Thing\n\n# after\nmodule PuppetX\n  module MyModule\n    class Thing; end\n  end\nend","handlingStrategy":"validation","validationCode":"# Ruby — verify the constant exists after an explicit require, before relying on ClassLoader\npath = 'puppet_x/mymod/thing'\nrequire path\nunless Object.const_defined?('PuppetX::MyMod::Thing', false)\n  raise LoadError, \"#{path} loaded but PuppetX::MyMod::Thing is not defined — check nesting/casing\"\nend","typeGuard":"def resolves_to_module?(name_path)\n  name_path.reduce(Object) do |ns, n|\n    return false unless ns.const_defined?(n, false)\n    ns.const_get(n, false)\n  end.is_a?(Module)\nend","tryCatchPattern":"begin\n  Puppet::Pops::Types::ClassLoader.provide(name)\nrescue RuntimeError => e\n  # report file vs constant mismatch instead of failing opaquely\n  raise LoadError, \"#{e.message} — verify the file defines #{name} with exact nesting and casing\"\nend","preventionTips":["Develop modules on a case-sensitive filesystem (Linux) so file/constant casing bugs surface early.","Add a smoke test per module that requires each implementation file and asserts the constant is defined."],"tags":["puppet","class-loading","autoloader","ruby-constants","module-structure"],"backgroundTag":"class-autoload-mismatch","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}