{"record":{"id":"902c7628c9235996","repo":"fluent/fluentd","slug":"value-must-be-string-value","errorCode":null,"errorMessage":"value must be string: #{value}","messagePattern":"value must be string: #(.+?)","errorType":"exception","errorClass":"Fluent::TimeParser::TimeParseError","httpStatus":null,"severity":"error","filePath":"lib/fluent/time.rb","lineNumber":270,"sourceCode":"                 else\n                   ->(v) { t = strptime.exec(v); Fluent::EventTime.new(t.to_i + t.utc_offset - utc_offset, t.nsec) }\n                 end\n               when format then\n                 if utc_offset.nil?\n                   ->(v){ t = Time.strptime(v, format); Fluent::EventTime.new(t.to_i, t.nsec) }\n                 elsif utc_offset.respond_to?(:call)\n                   ->(v){ t = Time.strptime(v, format); Fluent::EventTime.new(t.to_i + t.utc_offset - utc_offset.call(t), t.nsec) }\n                 else\n                   ->(v){ t = Time.strptime(v, format); Fluent::EventTime.new(t.to_i + t.utc_offset - utc_offset, t.nsec) }\n                 end\n               else ->(v){ Fluent::EventTime.parse(v) }\n               end\n    end\n\n    # TODO: new cache mechanism using format string\n    def parse(value)\n      unless value.is_a?(String)\n        raise TimeParseError, \"value must be string: #{value}\"\n      end\n\n      if @cache1_key == value\n        return @cache1_time\n      elsif @cache2_key == value\n        return @cache2_time\n      else\n        begin\n          time = @parse.call(value)\n        rescue => e\n          raise TimeParseError, \"invalid time format: value = #{value}, error_class = #{e.class.name}, error = #{e.message}\"\n        end\n        @cache1_key = @cache2_key\n        @cache1_time = @cache2_time\n        @cache2_key = value\n        @cache2_time = time\n        return time\n      end","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/fluent/fluentd/blob/dd45c6e18dc7be33b5e5a0f0767bf46307ff5626/lib/fluent/time.rb#L252-L288","documentation":"Fluent::TimeParseError raised by TimeParser#parse (lib/fluent/time.rb:270) when the value passed is not a Ruby String. StringTimeParser (and strptime) can only handle strings, so an Integer, Float, Hash, or nil arriving at parse() is a hard type error, distinct from an unparseable-but-string value which produces 'invalid time format' instead.","triggerScenarios":"Calling parser.parse(1629489132) or parser.parse(nil) on a Fluent::TimeParser configured for string formats; feeding a JSON payload where the time field is a JSON number into a parser built from time_type :string (the default) — e.g. in_http with record time as epoch integer and no time_type float/unixtime/mixed setting.","commonSituations":"Upstream producer changes a log field from string timestamp to epoch integer; a filter mutates the time field to a number before the parser runs; nil time field when a record lacks the time_key; using a parser plugin API directly with non-string input.","solutions":["If timestamps may be numeric, set time_type float or unixtime (NumericTimeParser accepts String and Numeric), or time_type mixed with unixtime in the fallbacks","Normalize the record before parsing: convert the field with value.to_s (or skip when nil) in a record_transformer/filter or custom parser","Ensure the time_key actually exists and is a string in the incoming records (fix the producer or an upstream parser)"],"exampleFix":"# before\n<parse>\n  @type json\n  time_key time     # producer now sends \"time\": 1629489132 (integer) -> TimeParseError\n</parse>\n\n# after\n<parse>\n  @type json\n  time_key time\n  time_type mixed\n  time_format_fallbacks unixtime, %iso8601\n</parse>","handlingStrategy":"type-guard","validationCode":"value = record['time']\nnext unless value.is_a?(String)   # skip or convert non-strings before parsing","typeGuard":"def parsable_string?(v)\n  v.is_a?(String) && !v.empty?\nend\n\nraise Fluent::TimeParseError unless parsable_string?(value)","tryCatchPattern":"begin\n  time = parser.parse(value)\nrescue Fluent::TimeParseError => e\n  log.warn 'skipping record with non-string time', value: value.inspect, error: e.message\n  next   # or re-tag to a dead-letter stream\nend","preventionTips":["Pin down the upstream contract: time field is always a string in the documented format","For numeric epochs configure time_type float/unixtime or mixed instead of the default string parser","Add a filter that drops/default-converts records whose time_key is nil or non-scalar"],"tags":["fluentd","time","parser","type-error","runtime"],"backgroundTag":"invalid-input-type","analyzedSha":"dd45c6e18dc7be33b5e5a0f0767bf46307ff5626","analyzedAt":"2026-08-21T16:22:07.332Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}