fluent/fluentd · error · ArgumentError

Input must be a map (got #{record.class})

Error message

Input must be a map (got #{record.class})

What it means

Raised as ArgumentError by Writer#write in fluent-cat. Each item parsed from stdin must be a Hash: by default one JSON object per line; with --msgpack an unpacked record; with --none the line is wrapped for you. The only non-Hash accepted shape is a two-element pair whose first element is a Fluent::EventTime and second is a Hash (msgpack secondary record). Any other JSON type (array, string, number, true/false/null) fails with the record's class in the message.

Source

Thrown at lib/fluent/command/cat.rb:171

    @retry_wait = 1
    @retry_limit = retry_limit
    @time_as_integer = time_as_integer
    @event_time = event_time

    super()
  end

  def secondary_record?(record)
    record.class != Hash &&
      record.size == 2 &&
      record.first.class == Fluent::EventTime &&
      record.last.class == Hash
  end

  def write(record)
    unless secondary_record?(record)
      if record.class != Hash
        raise ArgumentError, "Input must be a map (got #{record.class})"
      end
    end

    time = if @event_time
             Fluent::EventTime.parse(@event_time)
           else
             Fluent::EventTime.now
           end
    time = time.to_i if @time_as_integer
    entry = if secondary_record?(record)
              # Even though secondary contains Fluent::EventTime in record,
              # fluent-cat just ignore it and set Fluent::EventTime.now instead.
              # This specification is adopted to keep consistency.
              [time, record.last]
            else
              [time, record]
            end
    synchronize {

View on GitHub (pinned to dd45c6e18d)

Solutions

  1. Send one JSON object per line: echo '{"count": 1}' | fluent-cat my.tag
  2. For raw text use --none (each line becomes {"message": "..."}) and --message-key to rename the field
  3. Fix the upstream producer: jq -c '.[]' or similar to stream objects instead of one array
  4. For msgpack input, ensure every unpacked object is a Hash (or an EventTime/Hash pair)

Example fix

# before
echo '[1, 2]' | fluent-cat test.tag      # JSON array -> ArgumentError

# after
echo '{"one": 2}' | fluent-cat test.tag    # one JSON object per line
echo 'plain text' | fluent-cat --none test.tag
Defensive patterns

Strategy: type-guard

Validate before calling

records = lines.map { |l| JSON.parse(l) }.select { |r| r.is_a?(Hash) }
records.each { |r| writer.write(r) }

Type guard

def fluent_cat_record?(obj)
  obj.is_a?(Hash) ||
    (obj.respond_to?(:size) && obj.size == 2 &&
     obj.first.is_a?(Fluent::EventTime) && obj.last.is_a?(Hash))
end

Try / catch

begin
  writer.write(record)
rescue ArgumentError => e
  raise unless e.message.start_with?('Input must be a map')
  $stderr.puts "skipping non-object record: #{record.class}"
end

Prevention

When it happens

Trigger: echo '[1,2]' | fluent-cat test.tag or echo '"hello"' | fluent-cat test.tag (valid JSON but not an object); a producer emitting JSON arrays or scalars; a msgpack stream containing non-Hash objects; concatenating pretty-printed multi-line JSON.

Common situations: Piping jq output that is an array (jq '.items'); feeding NDJSON lines that are quoted strings; test harnesses sending raw numbers; msgpack dumps from other tools with mixed payload types.

Related errors


AI-assisted analysis of fluent/fluentd@dd45c6e18d (2026-08-21). Data as JSON: /api/errors/b6e3ffa9ca1d1c8c. Report an issue: GitHub.