{"record":{"id":"29994e8a8b9d248e","repo":"fluent/fluentd","slug":"record-must-be-a-hash-record-class","errorCode":null,"errorMessage":"record must be a Hash: #{record.class}","messagePattern":"record must be a Hash: #(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/fluent/plugin/output.rb","lineNumber":940,"sourceCode":"          execute_chunking(tag, es, enqueue: (@flush_mode == :immediate))\n          if !@retry && @buffer.queued?(nil, optimistic: true)\n            submit_flush_once\n          end\n        rescue\n          # TODO: separate number of errors into emit errors and write/flush errors\n          @num_errors_metrics.inc\n          raise\n        end\n      end\n\n      # TODO: optimize this code\n      def metadata(tag, time, record)\n        # this arguments are ordered in output plugin's rule\n        # Metadata 's argument order is different from this one (timekey, tag, variables)\n\n        raise ArgumentError, \"tag must be a String: #{tag.class}\" unless tag.nil? || tag.is_a?(String)\n        raise ArgumentError, \"time must be a Fluent::EventTime (or Integer): #{time.class}\" unless time.nil? || time.is_a?(Fluent::EventTime) || time.is_a?(Integer)\n        raise ArgumentError, \"record must be a Hash: #{record.class}\" unless record.nil? || record.is_a?(Hash)\n\n        if @chunk_keys.nil? && @chunk_key_time.nil? && @chunk_key_tag.nil?\n          # for tests\n          return Struct.new(:timekey, :tag, :variables).new\n        end\n\n        timekey = @chunk_key_time ? calculate_timekey(time) : nil\n        @_metadata_cache ||= MetadataCache.new\n\n        # timekey is int from epoch, and `timekey - timekey % 60` is assumed to mach with 0s of each minutes.\n        # it's wrong if timezone is configured as one which supports leap second, but it's very rare and\n        # we can ignore it (especially in production systems).\n        if @chunk_keys.empty?\n          return @_metadata_cache.metadata if @_metadata_cache.cached?(timekey: timekey, tag: tag)\n\n          meta = if @chunk_key_time && @chunk_key_tag\n                   @buffer.metadata(timekey: timekey, tag: tag)\n                 elsif @chunk_key_time","sourceCodeStart":922,"sourceCodeEnd":958,"githubUrl":"https://github.com/fluent/fluentd/blob/dd45c6e18dc7be33b5e5a0f0767bf46307ff5626/lib/fluent/plugin/output.rb#L922-L958","documentation":"The third argument guard in Output#metadata (lib/fluent/plugin/output.rb:940): the record must be nil or a Hash, otherwise ArgumentError. Records are the fundamental key-value unit in fluentd, and the chunking layer keys buffers by record contents, so a non-Hash cannot be chunked. Most often this indicates a structural bug (wrong variable passed) rather than bad data.","triggerScenarios":"Calling output.metadata(tag, time, 'raw string') or metadata(tag, time, [time, record]) (passing the MessagePackEventStream entry pair instead of the record); passing an Array of records or an OpenStruct; a custom filter returning non-Hash entries from its mutators.","commonSituations":"Custom output plugin development and unit tests constructing Metadata by hand; porting v0-era plugin code where arrays of [tag, time, record] were iterated incorrectly; feeding a OneEventStream's unpacked tuple into the wrong parameter.","solutions":["Pass the single record Hash (the third element of the usual tag/time/record triple).","If iterating an array like [tag, time, record], destructure with tag, time, record = entry instead of passing the entry wholesale.","Add an assertion/guard in custom plugins: raise or skip unless record.is_a?(Hash) before calling metadata."],"exampleFix":"# before\nentry = ['a.b', time, {'k' => 1}]\nmeta = output.metadata('a.b', time, entry)   # Array in record slot -> ArgumentError\n# after\n_tag, _time, record = entry\nmeta = output.metadata('a.b', time, record)","handlingStrategy":"type-guard","validationCode":"unless record.is_a?(Hash) || record.nil?\n  record = {'value' => record.to_s}\nend\nmeta = output.metadata(tag, time, record)","typeGuard":"def fluent_record?(r)\n  r.is_a?(Hash) || r.nil?\nend","tryCatchPattern":"rescue ArgumentError => e\n  raise unless e.message =~ /record must be a Hash/\n  log.error 'structural bug: non-Hash record reached chunking', record_class: record.class\n","preventionTips":["Destructure stream entries as tag, time, record before calling metadata.","Have filters return [tag, time, record] triples with the third element always a Hash.","Cover custom plugins with a test that emits one normal event end-to-end."],"tags":["fluentd","output-plugin","argument-validation","record-format"],"backgroundTag":"invalid-argument-type","analyzedSha":"dd45c6e18dc7be33b5e5a0f0767bf46307ff5626","analyzedAt":"2026-08-21T16:22:07.332Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}