Skip to content

bug: non-file outputs accept output.path and ignore it silently #1066

Closed

Description

@strawgate

Summary

output.path is accepted during validation for output types that do not use a file path (stdout, null, tcp, udp, http, otlp, elasticsearch, etc.). The field is silently ignored instead of being rejected as irrelevant.

Reproduction

This config validates successfully even though stdout is not a file sink:

input:
  type: file
  path: /dev/null
output:
  type: stdout
  path: /tmp/should-not-be-used

The same passes for other non-file outputs like otlp, http, tcp, udp, and elasticsearch.

Run:

git fetch origin
git switch --detach origin/master
cargo build -p logfwd
./target/debug/logfwd --config config.yaml --validate

Actual behavior

Validation succeeds:

ready: default
config ok: 1 pipeline(s)

Expected behavior

output.path should be rejected for output types that do not support it, instead of being silently ignored.

Affected commit

Observed on origin/master at df59f6bd143bc737a2d198c8b381bd3321114a28.

Activity

  1. github-actions commented on Apr 5, 2026

    @github-actions
    Contributor

    tl;dr I can reproduce this on master: output.path is currently accepted for multiple non-file outputs and ignored at runtime; the clean fix is to fail validation when path is set for output types that do not consume it.

    Recommendation

    Add an explicit validation rule in Config::validate() that rejects output.path for non-file outputs that do not use it (stdout, null, otlp, http, loki, tcp, udp, arrow_ipc).

    For elasticsearch, there is one nuance: runtime currently treats path as an alias for index. I recommend handling this explicitly (either keep as documented alias, or deprecate/remove alias and require index).

    Findings
    • OutputConfig exposes path for all outputs: crates/logfwd-config/src/lib.rs:250-261.
    • Validation does not reject irrelevant path for non-file outputs:
      • Output validation switch: crates/logfwd-config/src/lib.rs:693-760
      • Stdout | Null branch is empty: crates/logfwd-config/src/lib.rs:748
      • Endpoint-based outputs validate endpoint/compression/request_mode, but not extraneous path: crates/logfwd-config/src/lib.rs:694-739
    • Runtime sink construction ignores cfg.path for most non-file outputs:
      • Matcher entrypoint: crates/logfwd-output/src/lib.rs:662-790
      • stdout path unused: crates/logfwd-output/src/lib.rs:769-776
      • otlp path unused: crates/logfwd-output/src/lib.rs:740-767
    • Exception: Elasticsearch uses path as fallback for index:
      • crates/logfwd-output/src/lib.rs:668-673

    Relevant snippet:

    // crates/logfwd-output/src/lib.rs:668-673
    let index = cfg
        .index
        .as_ref()
        .or(cfg.path.as_ref())
        .map_or("logs", String::as_str)
        .to_string();
    Verification

    I ran these commands locally in the workspace:

    RUSTC_WRAPPER= cargo build -p logfwd --quiet
    ./target/debug/logfwd --config /tmp/gh-aw/agent/issue1066-stdout-path.yaml --validate
    ./target/debug/logfwd --config /tmp/gh-aw/agent/issue1066-otlp-path.yaml --validate

    Observed output:

    --- validate stdout+path ---
      ready: default
    config ok: 1 pipeline(s)
    --- validate otlp+path ---
      ready: default
    config ok: 1 pipeline(s)
    

    So both configs currently pass validation.

    Detailed Action Plan
    1. In crates/logfwd-config/src/lib.rs inside Config::validate() output loop (for (i, output) around :676+), add a pre-check after unsupported output-type checks:
      • If output.path.is_some() and output type is one of {Stdout, Null, Otlp, Http, Loki, Tcp, Udp, ArrowIpc}, return ConfigError::Validation.
    2. Decide Elasticsearch behavior explicitly:
      • Option A (strict): also reject path for Elasticsearch and require index only; remove .or(cfg.path.as_ref()) in crates/logfwd-output/src/lib.rs:671.
      • Option B (compat): keep alias and add a config test + docs note that path is legacy alias for index only for Elasticsearch.
    3. Add table-driven tests in crates/logfwd-config/src/lib.rs test module validating rejection message for each non-supported output type with path set.
    4. Add one positive Elasticsearch test matching whichever option is selected.
    Related Items
    Type Link Relevance
    Issue #1066 Current bug report
    Issue #530 Prior validation/runtime mismatch for output config
    Issue #718 Prior output validation inconsistency
    PR #795 Recent output validation changes tied to #530/#718
    Issue #1060 Similar class: stdout accepts invalid output-side field values
    File crates/logfwd-config/src/lib.rs Validation logic to update
    File crates/logfwd-output/src/lib.rs Runtime sink behavior (path usage/ignore)

    Note

    🔒 Integrity filtering filtered 4 items

    Integrity filtering activated and filtered the following items during workflow execution.
    This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.


    What is this? | From workflow: Issue Triage

    Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

  2. strawgate commented on Apr 5, 2026

    @strawgate
    OwnerAuthor

    Validation: CONFIRMED — shared root cause with all silent-ignore config issues

    Both InputConfig and OutputConfig are single flat structs (#[serde(deny_unknown_fields)]) with fields shared across all input/output types. Config::validate() checks that required fields are present for each type but does not reject extraneous optional fields that have no meaning for the selected type.

    This bug is one of ~12 confirmed instances of the same pattern (see also #1057, #1058, #1060, #1064, #1065, #1066, #1070, #1071, #1072, #1073, #1075, #1077, #1078).

    Root cause confirmed:

    • crates/logfwd-output/src/lib.rs: runtime builders use match cfg.output_type and silently ignore fields not consumed by the selected arm.
    • crates/logfwd-config/src/lib.rs Config::validate(): validates required fields per type but has no reject-if-present checks for inapplicable optional fields.

    Unified fix — add per-type field rejection to Config::validate():

    // Example for OutputConfig:
    match output.output_type {
        OutputType::Stdout | OutputType::Null => {
            if output.endpoint.is_some() { warn_or_err("endpoint not used by this output type"); }
            if output.index.is_some() { warn_or_err("index not used by this output type"); }
            if output.compression.is_some() { warn_or_err("compression not used by this output type"); }
            if output.auth.is_some() { warn_or_err("auth not used by this output type"); }
            // etc.
        }
        OutputType::Elasticsearch => {
            if output.protocol.is_some() { warn_or_err("protocol not used by elasticsearch"); }
            // etc.
        }
        // ...
    }

    A single pass over all output types covering all optional fields fixes all 12 related issues at once.

  3. strawgate commented on Apr 5, 2026

    @strawgate
    OwnerAuthor

    Fixed on master in PR #1123 (923a033bd8e855f4011f8ad9d5248b9e44a3546a).

    I rechecked origin/master; these type-inappropriate config fields are now rejected during validation, so this issue/work-unit looks stale and safe to close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions