puppetlabs/puppet · error · ArgumentError
Type alias '#{name}' cannot be resolved to a real type
Error message
Type alias '#{name}' cannot be resolved to a real type What it means
When a type alias is resolved, Puppet walks the resolved expression with AssertOtherTypeAcceptor to prove it contains at least one type that is not an alias or variant. An alias that resolves only to aliases or unresolved type references - a direct self-reference like 'type Alias = Alias', or a cycle such as 'type A = B; type B = A' - has no concrete meaning, so resolve raises ArgumentError and resets the alias to unresolved.
Source
Thrown at lib/puppet/pops/types/types.rb:3506
if @resolved_type.nil?
# resolved to PTypeReferenceType::DEFAULT during resolve to avoid endless recursion
@resolved_type = PTypeReferenceType::DEFAULT
@self_recursion = true # assumed while it being found out below
begin
if @type_expr.is_a?(PTypeReferenceType)
@resolved_type = @type_expr.resolve(loader)
else
@resolved_type = TypeParser.singleton.interpret(@type_expr, loader).normalize
end
# Find out if this type is recursive. A recursive type has performance implications
# on several methods and this knowledge is used to avoid that for non-recursive
# types.
guard = RecursionGuard.new
real_type_asserter = AssertOtherTypeAcceptor.new
accept(real_type_asserter, guard)
unless real_type_asserter.other_type_detected?
raise ArgumentError, "Type alias '#{name}' cannot be resolved to a real type"
end
@self_recursion = guard.recursive_this?(self)
# All aliases involved must re-check status since this alias is now resolved
if @self_recursion
accept(AssertSelfRecursionStatusAcceptor.new, RecursionGuard.new)
when_self_recursion_detected
end
rescue
@resolved_type = nil
raise
end
else
# An alias may appoint an Object type that isn't resolved yet. The default type
# reference is used to prevent endless recursion and should not be resolved here.
@resolved_type.resolve(loader) unless @resolved_type.equal?(PTypeReferenceType::DEFAULT)
end
selfView on GitHub (pinned to e227c27540)
Solutions
- Ground the cycle in at least one concrete type: type A = B plus type B = Integer.
- Check the right-hand side for a self-reference (same name as the alias being defined) and fix the target name.
- Use fully qualified names so the reference cannot resolve back to the alias.
Example fix
# before (module manifest) type MyType = MyAlias type MyAlias = MyType # cycle: ArgumentError at load # after type MyType = MyAlias type MyAlias = Integer # grounded: resolves fine
Defensive patterns
Strategy: try-catch
Validate before calling
# when generating aliases programmatically, reject self references first
fail "alias #{name} references itself" if expr.strip == name Try / catch
begin
parser.parse(type_definition_file) # module load resolves aliases
rescue ArgumentError => e
STDERR.puts "bad type alias in #{file}: #{e.message}" # points at the cyclic alias
end Prevention
- After copy-pasting an alias definition, always rewrite the right-hand side.
- Keep aliases grounded: every alias chain must end in a core type.
- Run puppet parser validate in CI so cyclic aliases fail at load time, not at apply time.
When it happens
Trigger: type MyType = MyType in a module; two or more aliases referencing each other with no concrete type anywhere in the cycle; renaming a type so an existing alias now points at its own name.
Common situations: Copy-paste when adding a new alias beside an existing one and forgetting to change the right-hand side; refactors and renames that introduce cycles; namespaced aliases whose short name resolves back to the alias itself.
Related errors
- Reference to unresolved type #{@name}
- Parsing of 'type "%{assignment_string}"' failed with message
- Expected a type alias assignment on the form 'AliasType = T'
- The parameter '$#{name}' is invalid: #{e.message}
- The code loaded from %{source_ref} does not define the type
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/2e5c9c7e3e0deb35.
Report an issue: GitHub.