fluent/fluentd · critical · Fluent::UnrecoverableError
Invalid path component detected, replaced to: #{rvalue}
Error message
Invalid path component detected, replaced to: #{rvalue} What it means
Companion guard to error 220 in Output#extract_placeholders: after all placeholder substitution, it rescans the finished path (rvalue) with PARENT_DIRECTORY_PATTERN (%r{\.\.[/\\]}) and raises Fluent::UnrecoverableError if the '../' count grew compared to the original template (str). This catches indirect escapes that the per-value check misses, e.g. a substituted value that is exactly '..' (no trailing slash) combining with adjacent template text to form extra '../' segments. As an UnrecoverableError it discards the affected buffer chunk.
Source
Thrown at lib/fluent/plugin/output.rb:885
@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
end
def emit_events(tag, es)
# actually this method will be overwritten by #configure
if @buffering
emit_buffered(tag, es)
else
emit_sync(tag, es)View on GitHub (pinned to dd45c6e18d)
Solutions
- Remove '..' segments from the path template itself and use absolute, flat buffer paths.
- Reject or rewrite chunk-key values equal to '..' or containing '..' in a filter before buffering (record['a'] == '..' ? '_' : record['a']).
- Replace user-controlled path segments with ${chunk_id} or a computed digest.
- Add a config-time lint: assert that path.scan(%r{\.\.[/\\]}).size plus any substituted value's count never exceeds the template's.
Example fix
# before
path /var/log/fluent/${tenant}/../archive
# after
path /var/log/fluent/archive/${tenant} Defensive patterns
Strategy: validation
Validate before calling
# count guard mirroring the core check: substituted value must not add '../' segments
PARENT = %r{\.\.[/\\]}
raise_if_escape = value.scan(PARENT).size > 0 || value == '..'
# rewrite instead of raising:
value = '_' if value == '..'
value = value.gsub(PARENT, '_') Type guard
def safe_template_value?(v)
v.is_a?(String) && v != '..' && !v.match?(%r{\.\.[/\\]})
end Try / catch
rescue Fluent::UnrecoverableError => e log.error 'chunk discarded, path escapes buffer dir', replaced: e.message # ship the raw record to a fallback label for manual repair
Prevention
- Keep buffer path templates free of '..' segments entirely.
- Whitelist chunk-key values to a known-safe set when they map to directories.
- Test configs with adversarial values ('..', 'a/../b', '/abs') before rollout.
When it happens
Trigger: Template path /srv/${a}/../share with a record where chunk key a='..' yields /srv/../../share (2 vs 1 '../' occurrences) and raises. Also values like 'x..' or '..y' inside templates that already contain '../' when substitution increases the count.
Common situations: Buffer path templates that legitimately contain '..' segments combined with user-controlled chunk keys; symlink-style path templates ('/var/log/${svc}/current') fed with values like '..'; post-upgrade configs that previously resolved outside the buffer directory without complaint.
Related errors
- Invalid path component detected in #{matched}: #{replace}
- <buffer> section is configured, but plugin '#{self.class}' d
- secondary plugin '#{self.class}' must support buffering, but
- tag must be a String: #{tag.class}
- this plugin '#{self.class}' cannot handle arguments for <buf
AI-assisted analysis of fluent/fluentd@dd45c6e18d (2026-08-21).
Data as JSON: /api/errors/a28dc8368c47a41c.
Report an issue: GitHub.