fluent/fluentd · error · ArgumentError

Template for formatter must be a Class or callable object

Error message

Template for formatter must be a Class or callable object

What it means

Raised as ArgumentError by the legacy v0.12 registration helper Fluent::Compat::TextFormatter.register_template(type, template). It accepts three shapes: a formatter Class (registered directly), a callable with arity 3 (wrapped in ProcWrappedFormatter and invoked as |tag, time, record|), or any other callable (used as a factory to instantiate formatters). Anything else - a formatter instance, a String, a non-callable object - is rejected.

Source

Thrown at lib/fluent/compat/formatter.rb:47

require 'fluent/plugin/formatter_single_value'

module Fluent
  module Compat
    class Formatter < Fluent::Plugin::Formatter
      # TODO: warn when deprecated
    end

    module TextFormatter
      def self.register_template(type, template)
        # TODO: warn when deprecated to use Plugin.register_formatter directly
        if template.is_a?(Class)
          Fluent::Plugin.register_formatter(type, template)
        elsif template.respond_to?(:call) && template.arity == 3 # Proc.new { |tag, time, record| }
          Fluent::Plugin.register_formatter(type, Proc.new { ProcWrappedFormatter.new(template) })
        elsif template.respond_to?(:call)
          Fluent::Plugin.register_formatter(type, template)
        else
          raise ArgumentError, "Template for formatter must be a Class or callable object"
        end
      end

      def self.lookup(type)
        # TODO: warn when deprecated to use Plugin.new_formatter(type, parent: plugin)
        Fluent::Plugin.new_formatter(type)
      end

      # Keep backward-compatibility
      def self.create(conf)
        # TODO: warn when deprecated
        format = conf['format']
        if format.nil?
          raise ConfigError, "'format' parameter is required"
        end

        formatter = lookup(format)
        if formatter.respond_to?(:configure)

View on GitHub (pinned to dd45c6e18d)

Solutions

  1. Pass the class: Fluent::Compat::TextFormatter.register_template('my_fmt', MyFormatter)
  2. Pass a Proc: Proc.new { |tag, time, record| ... } (arity 3) or a factory proc that returns formatter instances
  3. Migrate to the v1 API: Fluent::Plugin.register_formatter('my_fmt', MyFormatter)

Example fix

# before
Fluent::Compat::TextFormatter.register_template('my_fmt', MyFormatter.new)

# after
Fluent::Compat::TextFormatter.register_template('my_fmt', MyFormatter)
# or the v1 API:
Fluent::Plugin.register_formatter('my_fmt', MyFormatter)
Defensive patterns

Strategy: type-guard

Validate before calling

raise ArgumentError, 'template must be a Class or callable' unless template.is_a?(Class) || template.respond_to?(:call)
Fluent::Compat::TextFormatter.register_template(type, template)

Type guard

def formatter_template?(t)
  t.is_a?(Class) || (t.respond_to?(:call) && [0, -1, 3].include?(t.arity))
end

Try / catch

begin
  Fluent::Compat::TextFormatter.register_template(type, template)
rescue ArgumentError => e
  raise unless e.message.include?('must be a Class or callable object')
  Fluent::Compat::TextFormatter.register_template(type, template.class)
end

Prevention

When it happens

Trigger: TextFormatter.register_template('my_fmt', MyFormatter.new) (instance instead of class); passing a configuration Hash or String; an object that does not respond to #call.

Common situations: Porting v0.12-era plugin code that assumed a different registration convention; copy-pasting registration lines between plugins; mixing new Fluent::Plugin.register_formatter with old TextFormatter APIs.

Related errors


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