puppetlabs/puppet · error · Puppet::Indirector::ValidationError
Invalid instance type %{klass}, expected %{model_type}
Error message
Invalid instance type %{klass}, expected %{model_type} What it means
Terminus#validate_model uses `model === instance` to check that the object attached to a request is an instance of (or duck-compatible with) the indirection's model class. Saving a foreign object raises Puppet::Indirector::ValidationError naming the actual class and the expected model. This protects termini such as the YAML store from serializing an object of the wrong kind under an indirection (e.g., a Puppet::Node handed to the facts terminus).
Source
Thrown at lib/puppet/indirector/terminus.rb:177
self.class.terminus_type
end
def validate(request)
if request.instance
validate_model(request)
validate_key(request)
end
end
def validate_key(request)
unless request.key == request.instance.name
raise Puppet::Indirector::ValidationError, _("Instance name %{name} does not match requested key %{key}") % { name: request.instance.name.inspect, key: request.key.inspect }
end
end
def validate_model(request)
unless model === request.instance
raise Puppet::Indirector::ValidationError, _("Invalid instance type %{klass}, expected %{model_type}") % { klass: request.instance.class.inspect, model_type: model.inspect }
end
end
end
View on GitHub (pinned to e227c27540)
Solutions
- Construct the correct model for the indirection (facts -> Puppet::Node::Facts, node -> Puppet::Node, catalog -> Puppet::Resource::Catalog)
- Check the error message: %{klass} tells you what you actually passed; convert it before saving
- In tests, build real (or properly subclassed) instances instead of pure doubles when exercising save paths
- Use each indirection's own factory methods (Puppet::Node::Facts.new(...)) rather than generic serializers
Example fix
# before
node = Puppet::Node.new('web01')
Puppet::Node::Facts.indirection.save(node, 'web01')
# => Invalid instance type Puppet::Node, expected Puppet::Node::Facts
# after
facts = Puppet::Node::Facts.new('web01', values)
Puppet::Node::Facts.indirection.save(facts, 'web01') Defensive patterns
Strategy: type-guard
Validate before calling
expected = Puppet::Node::Facts # the indirection's model
unless expected === instance
raise Puppet::Indirector::ValidationError, "expected #{expected}, got #{instance.class}"
end Type guard
def valid_model_instance?(instance, model_class) model_class === instance end
Prevention
- Construct objects with the model class named in the indirection
- In RSpec, use instance_double(Puppet::Node::Facts) so contract violations fail at test time
- Centralize save calls in one helper per indirection so the model pairing is checked once
When it happens
Trigger: Puppet::Node::Facts.indirection.save(node_instance, key) where node_instance is a Puppet::Node; passing a String or Hash where a model instance is required; stub/mock objects in tests that do not inherit from the model class.
Common situations: Refactorings that change which class is constructed but not which indirection saves it; copy-pasted code between facts and node handling; RSpec doubles substituted for real instances without verifying the model contract.
Related errors
- Instance name %{name} does not match requested key %{key}
- invalid key
- #{subject} #{what},
- One or more file(s) specified did not exist: %{files}
- a data type must have an interface
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/7770ae2df2a2d408.
Report an issue: GitHub.