fluent/fluentd · error · Fluent::ConfigError

invalid regexp: missing right slash: #{str}

Error message

invalid regexp: missing right slash: #{str}

What it means

For parameters of the :regexp type, a value starting with '/' is treated as a delimited regex literal: the closing '/' must appear within the last three characters so that at most two flag characters ('i', 'm') may follow it. regexp_value computes the last '/' via rindex and raises this ConfigError when that slash sits too far from the end, i.e. the literal is effectively unterminated. Without a leading slash the whole string is compiled directly as a regex, so only the delimited form has this syntax requirement.

Source

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

        # Current parser passes comment without actual values, e.g. "param #foo".
        # parser should pass empty string in this case but changing behaviour may break existing environment so keep parser behaviour. Just ignore comment value in boolean handling for now.
        if str.respond_to?(:start_with?) && str.start_with?('#')
          true
        elsif opts[:strict]
          raise Fluent::ConfigError, "#{name}: invalid bool value: #{str}"
        else
          nil
        end
      end
    end

    def self.regexp_value(str, opts = {}, name = nil)
      return nil unless str

      return Regexp.compile(str) unless str.start_with?("/")
      right_slash_position = str.rindex("/")
      if right_slash_position < str.size - 3
        raise Fluent::ConfigError, "invalid regexp: missing right slash: #{str}"
      end
      options = str[(right_slash_position + 1)..-1]
      option = 0
      option |= Regexp::IGNORECASE if options.include?("i")
      option |= Regexp::MULTILINE if options.include?("m")
      Regexp.compile(str[1...right_slash_position], option)
    end

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

      v = val.to_s
      v = v.frozen? ? v.dup : v # config_param can't assume incoming string is mutable
      v.force_encoding(Encoding::UTF_8)
    end

    STRING_TYPE = Proc.new { |val, opts = {}, name = nil|
      Config.string_value(val, opts, name)

View on GitHub (pinned to dd45c6e18d)

Solutions

  1. Close the literal: '/app\.log/' or with flags '/app\.log/i'
  2. If the pattern is not meant to be a delimited regex, drop the leading slash so the raw string is compiled as-is (only flags are then unavailable)
  3. For tags that genuinely start with '/', write a full delimited regex that matches them, e.g. '/\/var\/log\/.*/'
  4. Double-check escaping: the content between the slashes is passed to Regexp.compile verbatim

Example fix

# before
# config_param :exclude, :regexp
<match demo.**>
  @type stdout
  exclude /debug
</match>

# after
<match demo.**>
  @type stdout
  exclude /debug/
</match>
Defensive patterns

Strategy: validation

Validate before calling

s = raw_value.to_s
terminated = !s.start_with?('/') || (idx = s.rindex('/')) && idx >= s.size - 3
raise ArgumentError, "unterminated regexp literal: #{s.inspect}" unless terminated
Regexp.compile(s[1...s.rindex('/')]) rescue raise ArgumentError, "bad regexp body: #{s.inspect}"

Prevention

When it happens

Trigger: config_param :pattern, :regexp with a value like '/app\.log' (opening slash but no closing slash), '/logs?' with no terminator, or a leading-slash tag glob written as '/var/log/*' that is misparsed as an unterminated regex literal.

Common situations: Forgetting the trailing slash in <match>-adjacent regex params; tags that literally start with '/' (syslog-style, some Docker logging drivers) colliding with the delimited-regex syntax; hand-editing configs where the glob was copied from a path.

Related errors


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