{"record":{"id":"313b39e4cf5775e7","repo":"puppetlabs/puppet","slug":"member-attempts-to-override-final-label","errorCode":null,"errorMessage":"%{member} attempts to override final %{label}","messagePattern":"%(.+?) attempts to override final %(.+?)","errorType":"exception","errorClass":"Puppet::ParseError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/types/p_object_type.rb","lineNumber":177,"sourceCode":"        end\n        self\n      else\n        parent_member.assert_can_be_overridden(self)\n      end\n    end\n\n    # Checks if the given _member_ can override this member.\n    #\n    # @param member [PAnnotatedMember] the overriding member\n    # @return [PAnnotatedMember] its argument\n    # @raises [Puppet::ParseError] if the assertion fails\n    # @api private\n    def assert_can_be_overridden(member)\n      unless instance_of?(member.class)\n        raise Puppet::ParseError, _(\"%{member} attempts to override %{label}\") % { member: member.label, label: label }\n      end\n      if @final && !(constant? && member.constant?)\n        raise Puppet::ParseError, _(\"%{member} attempts to override final %{label}\") % { member: member.label, label: label }\n      end\n      unless member.override?\n        # TRANSLATOR 'override => true' is a puppet syntax and should not be translated\n        raise Puppet::ParseError, _(\"%{member} attempts to override %{label} without having override => true\") % { member: member.label, label: label }\n      end\n      unless @type.assignable?(member.type)\n        raise Puppet::ParseError, _(\"%{member} attempts to override %{label} with a type that does not match\") % { member: member.label, label: label }\n      end\n\n      member\n    end\n\n    def constant?\n      false\n    end\n\n    # @return [Boolean] `true` if this feature cannot be overridden\n    # @api public","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/types/p_object_type.rb#L159-L195","documentation":"Members declared final => true in an Object type are closed for extension: a child type that attempts to override one gets a Puppet::ParseError. The only exception in the source is a final constant overridden by another constant (constant? && member.constant?), since constants are resolved statically.","triggerScenarios":"Parent attribute declared with 'final' => true; child type declares an attribute with the same name and 'override' => true. The final check fires before the override-flag and type checks.","commonSituations":"Extending third-party or core data types whose authors marked members final to freeze the contract; upstream version upgrades that newly mark a member final and break previously-working child types.","solutions":["Do not override the final member — add a differently named member in the child type instead.","Use composition: wrap the parent type in a new Object type rather than inheriting from it.","If you own the parent type, remove 'final' => true there (accepting that all subclasses see the contract change)."],"exampleFix":"# before — Base declares 'id' with final => true\ntype Sub = Object[{'parent' => 'Base', 'attributes' => {'id' => {'type' => String, 'override' => true}}}]\n\n# after\ntype Sub = Object[{'parent' => 'Base', 'attributes' => {'sub_id' => String}}]","handlingStrategy":"validation","validationCode":"# Ruby — before generating an override, check the inherited member is not final\ninherited = parent_members[name]\nfail(\"#{name} is final and cannot be overridden\") if inherited&.final? && !(inherited.constant? && entry['kind'] == 'constant')","typeGuard":"def overridable_member?(member)\n  !member.final? || (member.constant? && member.constant?) # constant-over-constant is the only exception\nend","tryCatchPattern":"begin\n  Puppet::Pops::Types::PObjectType.new(name, hash)\nrescue Puppet::ParseError => e\n  fail(\"cannot extend final member: #{e.message} — use a new attribute name instead\")\nend","preventionTips":["Treat final as a published contract freeze; plan composition instead of inheritance for such types.","When upgrading dependencies, diff final flags on parent types your types extend."],"tags":["puppet","object-type","inheritance","final","override"],"backgroundTag":"cannot-override-final-member","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}