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
    self

View on GitHub (pinned to e227c27540)

Solutions

  1. Ground the cycle in at least one concrete type: type A = B plus type B = Integer.
  2. Check the right-hand side for a self-reference (same name as the alias being defined) and fix the target name.
  3. 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

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


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