{"record":{"id":"0e8ef70548f41020","repo":"puppetlabs/puppet","slug":"field-field-is-required","errorCode":null,"errorMessage":"Field '%{field}' is required","messagePattern":"Field '%(.+?)' is required","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/fileparsing.rb","lineNumber":114,"sourceCode":"          instance_eval(&block)\n        else\n          meta_def(@block_eval, &block)\n        end\n      end\n    end\n\n    # Convert a record into a line by joining the fields together appropriately.\n    # This is pulled into a separate method so it can be called by the hooks.\n    def join(details)\n      joinchar = self.joiner\n\n      fields.filter_map { |field|\n        # If the field is marked absent, use the appropriate replacement\n        if details[field] == :absent or details[field] == [:absent] or details[field].nil?\n          if self.optional.include?(field)\n            self.absent\n          else\n            raise ArgumentError, _(\"Field '%{field}' is required\") % { field: field }\n          end\n        else\n          details[field].to_s\n        end\n      }.join(joinchar)\n    end\n\n    # Customize this so we can do a bit of validation.\n    def optional=(optional)\n      @optional = optional.collect(&:intern)\n    end\n\n    # Create a hook that modifies the hash resulting from parsing.\n    def post_parse=(block)\n      meta_def(:post_parse, &block)\n    end\n\n    # Create a hook that modifies the hash just prior to generation.","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/fileparsing.rb#L96-L132","documentation":"When a FileParsing provider regenerates a file, FileRecord#join serializes each record back into a line; any field whose value is :absent, [:absent], or nil must be covered. If the field is not listed in :optional, join raises ArgumentError 'Field ... is required' and the write is aborted mid-generation.","triggerScenarios":"A parsed record missing a column that was declared required — for example an /etc/fstab line with only four fields while the provider's record_line declared five (like :dump or :pass) — and Puppet then rewrites the file to add or remove an entry. Also a custom process/to_line hook leaving a required field nil.","commonSituations":"Malformed system files (fstab entries missing the dump/pass columns), hand-edited config lines, and providers moving between Puppet versions whose optional lists changed.","solutions":["Fix or normalize the source file so every required field is present","Declare genuinely omissible fields as optional in record_line: optional: %i[dump pass]","Pre-validate the record hash and fill defaults before serialization"],"exampleFix":"# before\nrecord_line :fstab, fields: %i[device mount fstype options dump pass], separator: /\\s+/\n# line 'tmpfs /tmp tmpfs defaults' => ArgumentError: Field 'dump' is required\n\n# after\nrecord_line :fstab, fields: %i[device mount fstype options dump pass], separator: /\\s+/,\n  optional: %i[dump pass], absent: '0'","handlingStrategy":"validation","validationCode":"filled = details.reject { |_, v| v.nil? || v == :absent || v == [:absent] }\nmissing = record.fields - record.optional - filled.keys\nraise ArgumentError, \"missing #{missing.join(', ')}\" unless missing.empty?","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Audit the target file for short lines before letting the provider rewrite it","Mark genuinely optional columns with optional: [...] in record_line","Fill defaults for absent values instead of relying on serialization to complain"],"tags":["puppet","provider","file-parsing","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}