fluent/fluentd · error · Fluent::TimeParser::TimeParseError

value must be a string or a number: #{value}(#{value.class})

Error message

value must be a string or a number: #{value}(#{value.class})

What it means

Fluent::TimeParseError raised by NumericTimeParser#parse_unixtime (lib/fluent/time.rb:308) when the value is neither a String nor a Numeric — for example a Hash, Array, true/false, or nil. This is the type-gate in front of epoch-seconds parsing; valid Strings are then coerced with to_i, so unparseable-but-numeric-looking strings do not raise here.

Source

Thrown at lib/fluent/time.rb:308

    alias :call :parse
  end

  class NumericTimeParser < TimeParser # to include TimeParseError
    def initialize(type, localtime = nil, timezone = nil)
      @cache1_key = @cache1_time = @cache2_key = @cache2_time = nil

      if type == :unixtime
        define_singleton_method(:parse, method(:parse_unixtime))
        define_singleton_method(:call, method(:parse_unixtime))
      else # :float
        define_singleton_method(:parse, method(:parse_float))
        define_singleton_method(:call, method(:parse_float))
      end
    end

    def parse_unixtime(value)
      unless value.is_a?(String) || value.is_a?(Numeric)
        raise TimeParseError, "value must be a string or a number: #{value}(#{value.class})"
      end

      if @cache1_key == value
        return @cache1_time
      elsif @cache2_key == value
        return @cache2_time
      end

      begin
        time = Fluent::EventTime.new(value.to_i)
      rescue => e
        raise TimeParseError, "invalid time format: value = #{value}, error_class = #{e.class.name}, error = #{e.message}"
      end
      @cache1_key = @cache2_key
      @cache1_time = @cache2_time
      @cache2_key = value
      @cache2_time = time
      time

View on GitHub (pinned to dd45c6e18d)

Solutions

  1. Validate the field exists and is String/Numeric before it reaches the parser (record_filter or custom filter)
  2. Fix the producer so time is epoch seconds/epoch string, not a structured object or null
  3. For strings like '2021-08-20T...' use a string time_type with formats instead of unixtime, or mixed with fallbacks

Example fix

# before
<parse>
  @type json
  time_key time
  time_type unixtime   # record: {"time": null} -> TimeParseError
</parse>

# after
<parse>
  @type json
  time_key time
  time_type unixtime
  estimate_current_event false
</parse>
# plus a filter dropping records without a usable time:
<filter **>
  @type record_transformer
  remove_keys _dummy_
</filter>
Defensive patterns

Strategy: type-guard

Validate before calling

value = record['time']
next unless value.is_a?(String) || value.is_a?(Numeric)

Type guard

def unixtime_like?(v)
  v.is_a?(String) || v.is_a?(Numeric)
end

Try / catch

begin
  t = numeric_parser.parse(value)
rescue Fluent::TimeParseError => e
  log.warn 'bad time field', value: value.inspect, error: e.message
  next
end

Prevention

When it happens

Trigger: time_type unixtime configured and a record's time field is a JSON object/array/boolean/null (e.g. {"time": null} or {"time": {"$date": ...}}); calling parser.parse(nil) or parser.parse({}) on a NumericTimeParser built for :unixtime.

Common situations: MongoDB-style extended JSON dates ($date wrapper) arriving as a Hash; producer omits the time field making it nil; a filter replaces the time field with a structured value; API changes turning the field into a nested object.

Related errors


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