{"record":{"id":"62b797232b816058","repo":"fluent/fluentd","slug":"template-for-formatter-must-be-a-class-or-callable","errorCode":null,"errorMessage":"Template for formatter must be a Class or callable object","messagePattern":"Template for formatter must be a Class or callable object","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/fluent/compat/formatter.rb","lineNumber":47,"sourceCode":"require 'fluent/plugin/formatter_single_value'\n\nmodule Fluent\n  module Compat\n    class Formatter < Fluent::Plugin::Formatter\n      # TODO: warn when deprecated\n    end\n\n    module TextFormatter\n      def self.register_template(type, template)\n        # TODO: warn when deprecated to use Plugin.register_formatter directly\n        if template.is_a?(Class)\n          Fluent::Plugin.register_formatter(type, template)\n        elsif template.respond_to?(:call) && template.arity == 3 # Proc.new { |tag, time, record| }\n          Fluent::Plugin.register_formatter(type, Proc.new { ProcWrappedFormatter.new(template) })\n        elsif template.respond_to?(:call)\n          Fluent::Plugin.register_formatter(type, template)\n        else\n          raise ArgumentError, \"Template for formatter must be a Class or callable object\"\n        end\n      end\n\n      def self.lookup(type)\n        # TODO: warn when deprecated to use Plugin.new_formatter(type, parent: plugin)\n        Fluent::Plugin.new_formatter(type)\n      end\n\n      # Keep backward-compatibility\n      def self.create(conf)\n        # TODO: warn when deprecated\n        format = conf['format']\n        if format.nil?\n          raise ConfigError, \"'format' parameter is required\"\n        end\n\n        formatter = lookup(format)\n        if formatter.respond_to?(:configure)","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/fluent/fluentd/blob/dd45c6e18dc7be33b5e5a0f0767bf46307ff5626/lib/fluent/compat/formatter.rb#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass the class: Fluent::Compat::TextFormatter.register_template('my_fmt', MyFormatter)","Pass a Proc: Proc.new { |tag, time, record| ... } (arity 3) or a factory proc that returns formatter instances","Migrate to the v1 API: Fluent::Plugin.register_formatter('my_fmt', MyFormatter)"],"exampleFix":"# before\nFluent::Compat::TextFormatter.register_template('my_fmt', MyFormatter.new)\n\n# after\nFluent::Compat::TextFormatter.register_template('my_fmt', MyFormatter)\n# or the v1 API:\nFluent::Plugin.register_formatter('my_fmt', MyFormatter)","handlingStrategy":"type-guard","validationCode":"raise ArgumentError, 'template must be a Class or callable' unless template.is_a?(Class) || template.respond_to?(:call)\nFluent::Compat::TextFormatter.register_template(type, template)","typeGuard":"def formatter_template?(t)\n  t.is_a?(Class) || (t.respond_to?(:call) && [0, -1, 3].include?(t.arity))\nend","tryCatchPattern":"begin\n  Fluent::Compat::TextFormatter.register_template(type, template)\nrescue ArgumentError => e\n  raise unless e.message.include?('must be a Class or callable object')\n  Fluent::Compat::TextFormatter.register_template(type, template.class)\nend","preventionTips":["Register classes, never instances","Use arity-3 procs only for tag/time/record style formatting","Prefer the v1 Fluent::Plugin.register_formatter API in new code"],"tags":["fluentd","formatter","plugin-registration","v012-compat"],"backgroundTag":"invalid-plugin-registration","analyzedSha":"dd45c6e18dc7be33b5e5a0f0767bf46307ff5626","analyzedAt":"2026-08-21T16:22:07.332Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}