{"record":{"id":"d8cd03e27c7e4430","repo":"fluent/fluentd","slug":"hash-required-but-got-val-inspect","errorCode":null,"errorMessage":"hash required but got #{val.inspect}","messagePattern":"hash required but got #(.+?)","errorType":"exception","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"lib/fluent/config/types.rb","lineNumber":209,"sourceCode":"        when :time then Config.time_value(value, opts, name)\n        when :regexp then Config.regexp_value(value, opts, name)\n        when :symbol then Config.symbol_value(value, opts, name)\n        else\n          raise \"unknown type in REFORMAT: #{type}\"\n        end\n      end\n    }\n\n    def self.hash_value(val, opts = {}, name = nil)\n      return nil if val.nil?\n\n      param = if val.is_a?(String)\n                val.start_with?('{') ? JSON.parse(val, Fluent::DEFAULT_JSON_PARSE_OPTIONS) : Hash[val.strip.split(/\\s*,\\s*/).map{|v| v.split(':', 2)}]\n              else\n                val\n              end\n      if param.class != Hash\n        raise ConfigError, \"hash required but got #{val.inspect}\"\n      end\n      if opts.empty?\n        param\n      else\n        newparam = {}\n        param.each_pair do |key, value|\n          new_key = opts[:symbolize_keys] ? key.to_sym : key\n          newparam[new_key] = opts[:value_type] ? REFORMAT_VALUE.call(opts[:value_type], value, opts, new_key) : value\n        end\n        newparam\n      end\n    end\n\n    HASH_TYPE = Proc.new { |val, opts = {}, name = nil|\n      Config.hash_value(val, opts, name)\n    }\n\n    def self.array_value(val, opts = {}, name = nil)","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/fluent/fluentd/blob/dd45c6e18dc7be33b5e5a0f0767bf46307ff5626/lib/fluent/config/types.rb#L191-L227","documentation":"hash_value accepts a JSON object string (starting with '{'), a 'k1:v1,k2:v2' comma/colon string, or a pre-built Hash, and raises ConfigError when the result is not a Hash. In practice the error fires when a non-String, non-Hash object is passed: an Array, Numeric, Symbol, or arbitrary object (e.g. from programmatic configuration or a reformat with value_type), because string inputs go down the parse paths. The check is param.class != Hash, so even a Hash subclass (ActiveSupport::HashWithIndifferentAccess, Hashie::Mash) is rejected.","triggerScenarios":"Passing [1, 2] or a Symbol to a :hash param when building Fluent::Config::Element programmatically or in plugin tests; handing a JSON array string where an object string was expected; passing a Hash subclass instead of a plain Hash; REFORMAT_VALUE with value_type :hash receiving a scalar element.","commonSituations":"RSpec tests configuring plugins with Ruby values instead of config strings; plugins consuming parsed JSON where the document is an array, not an object; app code that assumed duck-typed Hashes work.","solutions":["Pass a plain Hash (convert subclasses with to_h then re-wrap: Hash[obj.to_h]) or a 'k1:v1,k2:v2' string","For JSON input, ensure the top-level document is an object and serialize it with JSON.generate(hash)","When configuring programmatically, convert arrays to an index-keyed hash or change the param to :array","In tests, prefer config strings ('{\"k\":\"v\"}') so the same parse path as production runs"],"exampleFix":"# before\nplugin.configure(config_element('match', '**', { headers: [\"a\", \"b\"] }))\n\n# after\nplugin.configure(config_element('match', '**', { headers: 'a:1,b:2' }))","handlingStrategy":"type-guard","validationCode":"def hash_param_ok?(v)\n  return true if v.class == Hash\n  v.is_a?(String) && (v.start_with?('{') || v.include?(':'))\nend","typeGuard":"def hash_param_ok?(v)\n  return true if v.class == Hash\n  v.is_a?(String) && (v.start_with?('{') || v.include?(':'))\nend","tryCatchPattern":"begin\n  Fluent::Config.hash_value(val, opts, name)\nrescue Fluent::ConfigError => e\n  raise ConfigInvalid, \"#{name} must be a hash or 'k:v,k:v' string: #{e.message}\"\nend","preventionTips":["Pass plain Hashes (Hash[obj.to_h] for subclasses) or JSON object strings","In tests, configure with strings so production parse paths run","Validate parsed JSON top-level type before feeding a :hash param"],"tags":["fluentd","config","hash","type-validation"],"backgroundTag":"config-type-mismatch","analyzedSha":"dd45c6e18dc7be33b5e5a0f0767bf46307ff5626","analyzedAt":"2026-08-21T16:22:07.332Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}