fluent/fluentd · error · ConfigError

array required but got #{val.inspect}

Error message

array required but got #{val.inspect}

What it means

array_value accepts a comma-separated String (split into an Array of strings), an actual Array, and — as a documented convenience — wraps bare Numeric/true/false values produced by Psych/YAML; every other type falls through unchanged and fails the param.class != Array check, raising ConfigError. So Hashes, Symbols, Time objects, and arbitrary objects cannot be coerced into array params. The error is the guard against plugin defaults or programmatic config supplying the wrong shape.

Source

Thrown at lib/fluent/config/types.rb:244

    def self.array_value(val, opts = {}, name = nil)
      return nil if val.nil?

      param = if val.is_a?(String)
                val.start_with?('[') ? JSON.parse(val, Fluent::DEFAULT_JSON_PARSE_OPTIONS) : val.strip.split(/\s*,\s*/)
              elsif val.is_a?(Array)
                val
              elsif val.is_a?(Numeric) || val == true || val == false
                # Wrap only the bare scalars that Psych/YAML actually produces
                # here (nil is handled by the early return above). Any other
                # type (Hash, Symbol, Time, arbitrary objects) falls through and
                # still raises "array required" below.
                [val]
              else
                val
              end
      if param.class != Array
        raise ConfigError, "array required but got #{val.inspect}"
      end
      if opts[:value_type]
        param.map{|v| REFORMAT_VALUE.call(opts[:value_type], v, opts, nil) }
      else
        param
      end
    end

    ARRAY_TYPE = Proc.new { |val, opts = {}, name = nil|
      Config.array_value(val, opts, name)
    }
  end
end

View on GitHub (pinned to dd45c6e18d)

Solutions

  1. Pass an Array or a comma-separated string ('a,b,c')
  2. For programmatic values, map to strings and join: [:a, :b].map(&:to_s).join(',')
  3. Fix plugin defaults so :array params default to [] and never to a Hash
  4. If a mapping is genuinely needed, change the param declaration to :hash

Example fix

# before
# config_param :fields, :array
plugin.configure(config_element('match', '**', { fields: { "a" => 1 } }))

# after
plugin.configure(config_element('match', '**', { fields: "a,b" }))
Defensive patterns

Strategy: type-guard

Validate before calling

def array_param_ok?(v)
  v.is_a?(Array) || v.is_a?(String) || v.is_a?(Numeric) || v == true || v == false
end

Type guard

def array_param_ok?(v)
  v.is_a?(Array) || v.is_a?(String) || v.is_a?(Numeric) || v == true || v == false
end

Try / catch

begin
  Fluent::Config.array_value(val, opts, name)
rescue Fluent::ConfigError => e
  raise ConfigInvalid, "#{name} must be an array or comma-separated string: #{e.message}"
end

Prevention

When it happens

Trigger: A plugin default declared as a Hash for an :array param; programmatic/test configuration passing symbols ([:debug, :info]) or a Hash to a :array param; YAML/JSON-loaded config feeding a Struct/Time into the param.

Common situations: Writing plugin tests with Ruby objects instead of config strings; copy-pasting a hash literal where a list was intended; plugin author changing a param type without updating the default.

Related errors


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