puppetlabs/puppet · error · ArgumentError
attempt to #{action} #{type_name} annotation declared on #{o
Error message
attempt to #{action} #{type_name} annotation declared on #{o.label} What it means
Annotation.annotate_new(o, init_hash) forces creation or clearing of a Pcore annotation. When o is Annotatable (e.g. a Pcore type declaration or typeset member) and its annotations hash already contains this annotation type, the annotation is considered 'declared on the type' and is immutable at runtime: passing the CLEAR sentinel (the string 'clear') raises 'attempt to clear ...', any other init_hash raises 'attempt to redefine ...'. The message interpolates the annotation type name and o.label (the type/object label).
Source
Thrown at lib/puppet/pops/types/annotation.rb:55
end
adapter = associate_adapter(_pcore_type.from_hash(init_hash), o) unless init_hash.nil?
end
adapter
end
# Forces the creation or removal of an annotation of this type.
# If `init_hash` is a hash, a new annotation is created and returned
# If `init_hash` is `nil`, then the annotation is cleared and the previous annotation is returned.
#
# @param o [Object] object to annotate
# @param init_hash [Hash{String,Object},nil] the initializer for the annotation or `nil` to clear the annotation
# @return [Annotation<self>] an annotation of the same class as the receiver of the call
#
def self.annotate_new(o, init_hash)
if o.is_a?(Annotatable) && o.annotations.include?(_pcore_type)
# Prevent clear or redefine of annotations declared on type
action = init_hash == CLEAR ? 'clear' : 'redefine'
raise ArgumentError, "attempt to #{action} #{type_name} annotation declared on #{o.label}"
end
if init_hash == CLEAR
clear(o)
else
associate_adapter(_pcore_type.from_hash(init_hash), o)
end
end
# Uses name of type instead of name of the class (the class is likely dynamically generated and as such,
# has no name)
# @return [String] the name of the type
def self.type_name
_pcore_type.name
end
end
end
endView on GitHub (pinned to e227c27540)
Solutions
- Do not clear or redefine annotations that come from the type declaration - treat them as read-only metadata
- For per-instance mutable data, use a distinct annotation type not declared on the Annotatable type, and set it with associate_adapter-based paths (annotate with a block)
- Read the existing annotation instead of replacing it: MyAnnotation.annotate(o) returns the declared instance
- If you truly need different values, declare a new annotation type name rather than redefining the existing one
Example fix
// before
MyAnnotation.annotate_new(o, nil) # o declares MyAnnotation -> 'attempt to clear'
MyAnnotation.annotate_new(o, {'x' => 2}) # -> 'attempt to redefine'
// after
existing = MyAnnotation.annotate(o) # read the declared annotation
# mutable per-instance data goes in a separate, undeclared annotation type:
RuntimeInfo.annotate_new(o, {'x' => 2}) Defensive patterns
Strategy: validation
Validate before calling
# only set/clear annotations the type does NOT declare declared = o.is_a?(Puppet::Pops::Types::Annotatable) && o.annotations.include?(MyAnnotation._pcore_type) raise ArgumentError, 'annotation is declared on the type; read-only' if declared MyAnnotation.annotate_new(o, init_hash)
Type guard
def annotation_mutable?(o, ann_class) !(o.is_a?(Puppet::Pops::Types::Annotatable) && o.annotations.include?(ann_class._pcore_type)) end
Try / catch
begin
MyAnnotation.annotate_new(o, init_hash)
rescue ArgumentError => e
raise DataError, "declared annotation is immutable: #{e.message}" if e.message.start_with?('attempt to')
raise
end Prevention
- Treat type-declared annotations as read-only metadata; read them with MyAnnotation.annotate(o)
- Keep mutable per-instance state in a separate annotation type that the type does not declare
- Never re-annotate during serialization round-trips - reuse the existing adapter
When it happens
Trigger: Calling MyAnnotation.annotate_new(type_declaration, nil) or with a new init hash on an object whose type declaration already carries that annotation; Puppet DSL that mutates annotations on catalog-contained type declarations; code that treats declared annotations like per-instance adapters and tries to overwrite them.
Common situations: Module authors implementing rich data (Pcore) types who attempt to clear/redefine annotations during catalog compilation or rich-data round-trips; serialization code that annotates on load and re-annotates on save; confusion between per-object adapters (safe to set/clear) and type-declared annotations (protected).
Related errors
- puppet.tasks/unparseable-metadata
- Data Type Load Error for type '%{type_name}': %{message}
- a data type must have an interface
- a data type can only have one interface
- a data type can only have one implementation
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/afc72733aca9fe7b.
Report an issue: GitHub.