fluent/fluentd · critical · Fluent::UnrecoverableError

Invalid path component detected in #{matched}: #{replace}

Error message

Invalid path component detected in #{matched}: #{replace}

What it means

Fluentd raises this Fluent::UnrecoverableError inside Output#extract_placeholders when a buffer path placeholder (e.g. ${key} or ${tag}) is replaced by a chunk-key value containing a path-escape component. The guard tests the substituted value against INVALID_PATH_COMPONENT_PATTERN (%r{\.\.[/\\]|^[/\\]}, lib/fluent/plugin/output.rb:48), i.e. '../', '..\\', or a leading '/'/'\\'. UnrecoverableError means the chunk can never be flushed and is eventually discarded, so the events in it are lost. The check exists to stop record/tag contents from writing outside the configured buffer directory.

Source

Thrown at lib/fluent/plugin/output.rb:877

            else
              log.warn "${chunk_id} is not allowed in this plugin. Pass Chunk instead of metadata in extract_placeholders's 2nd argument"
            end
          }

          # Then, replace other ${chunk_key}s.
          if !@chunk_keys.empty? && metadata.variables
            hash = {'${tag}' => '${tag}'} # not to erase this wrongly
            @chunk_keys.each do |key|
              hash["${#{key}}"] = metadata.variables[key.to_sym]
            end

            rvalue = rvalue.gsub(CHUNK_KEY_PLACEHOLDER_PATTERN) do |matched|
              replace = hash.fetch(matched) do
                log.warn "chunk key placeholder '#{matched[2..-2]}' not replaced. template:#{str}"
                ''
              end
              if replace.to_s.match?(INVALID_PATH_COMPONENT_PATTERN)
                raise Fluent::UnrecoverableError, "Invalid path component detected in #{matched}: #{replace}"
              end

              replace
            end
            # Check if the number of parent directory components (../) has increased due to variable substitution
            if rvalue.match?(PARENT_DIRECTORY_PATTERN)
              if rvalue.scan(PARENT_DIRECTORY_PATTERN).size > str.scan(PARENT_DIRECTORY_PATTERN).size
                raise Fluent::UnrecoverableError, "Invalid path component detected, replaced to: #{rvalue}"
              end
            end
          end

          if rvalue =~ CHUNK_KEY_PLACEHOLDER_PATTERN
            log.warn "chunk key placeholder '#{$1}' not replaced. template:#{str}"
          end

          rvalue
        end

View on GitHub (pinned to dd45c6e18d)

Solutions

  1. Sanitize the chunk-key value before buffering: add a record_transformer/filter that strips '../', '..\\' and leading '/' (e.g. app ${record['app'].to_s.gsub(%r{\.\.[/\\]}, '_').sub(%r{^[/\\]}, '')}).
  2. Stop using raw record values in path templates: use ${chunk_id} or a digest/hash of the value as the directory name.
  3. Route records with malformed chunk-key values to a separate label/dead-letter output so only bad events are dropped.
  4. If nested directories under the buffer path are genuinely required, pre-map values to a fixed safe directory list instead of interpolating raw values.

Example fix

# before
<match logs.**>
  @type file
  path /var/log/fluent/${app}
  <buffer app>
    @type file
  </buffer>
</match>
# after: sanitize the chunk key before it becomes a path component
<filter logs.**>
  @type record_transformer
  enable_ruby true
  <record>
    app ${record['app'].to_s.gsub(%r{\.\.[/\\]}, '_').sub(%r{^[/\\]}, '')}
  </record>
</filter>
Defensive patterns

Strategy: validation

Validate before calling

# before events reach a buffered output with path placeholders:
INVALID_PATH = %r{\.\.[/\\]|^[/\\]}
if record['app'].to_s.match?(INVALID_PATH)
  router.emit_stream('app.invalid', OneEventStream.new([[Fluent::EventTime.now, record]]))
end

Type guard

def safe_path_component?(v)
  v.is_a?(String) && !v.match?(%r{\.\.[/\\]|^[/\\]})
end

Try / catch

rescue Fluent::UnrecoverableError => e
  log.error 'buffer chunk dropped: invalid path component', error: e
  # record to dead-letter; chunk is unrecoverable by design, do not retry

Prevention

When it happens

Trigger: A buffered output configured with <buffer app> and path /var/log/fluent/${app}, where a record's 'app' value is '../../etc/x', 'a/../..', 'C:\\evil', or '/abs/path'; likewise ${tag} when the input tag starts with '/' or contains '../'. Extraction happens at buffer-chunk creation, so the error surfaces on write/flush attempts.

Common situations: Using unvalidated request paths, container names, hostnames, or log file names as chunk keys in file/s3 outputs; forwarding untrusted events whose fields feed path templates; upgrading to newer fluentd where this traversal guard (INVALID_PATH_COMPONENT_PATTERN) was introduced and previously-tolerated values now fail.

Related errors


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