Skip to content

bug: tcp/udp inputs accept max_open_files and ignore it silently #1075

Closed

Description

@strawgate

Summary

Network inputs tcp and udp accept max_open_files during validation even though the setting only makes sense for file-based inputs. The field is silently ignored instead of being rejected as irrelevant.

Reproduction

This config validates successfully even though tcp has no open-file limit:

input:
  type: tcp
  listen: 127.0.0.1:19072
  max_open_files: 7
output:
  type: null

The same passes for udp input.

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

max_open_files should be rejected for network inputs 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

    max_open_files is currently accepted for tcp/udp and ignored at runtime; the fix should be a config-validation guard in logfwd-config that restricts it to file inputs.

    Recommendation

    Reject max_open_files whenever input_type != file (at least tcp/udp; ideally also generator/otlp/arrow_ipc for consistency with #1072), while keeping the existing max_open_files >= 1 check for file.

    Key files

    • crates/logfwd-config/src/lib.rs:233-245 — InputConfig defines max_open_files: Option(usize) with docstring “Applies only to file inputs.”
    • crates/logfwd-config/src/lib.rs:594-674 — Config::validate() input validation; currently only rejects max_open_files == 0 (:668-673), regardless of input type.
    • crates/logfwd/src/pipeline.rs:1269-1362 — build_input_state(...); max_open_files is consumed only in InputType::File (:1282-1287), not in udp/tcp branches (:1331-1361).
    • crates/logfwd-io/src/tcp_input.rs:79-81 — TcpInput::new(name, addr) signature has no open-file parameter.
    • crates/logfwd-io/src/udp_input.rs:34-35 — UdpInput::new(name, addr) signature has no open-file parameter.
    • crates/logfwd-io/src/tail.rs:132-160,963-964 — TailConfig.max_open_files and LRU eviction usage (file-only behavior).
    Findings
    1. The config layer exposes max_open_files on all input objects because InputConfig is shared across input types (crates/logfwd-config/src/lib.rs:233-245).
    2. Validation currently enforces only lower-bound (!= 0) and does not enforce applicability by input type (crates/logfwd-config/src/lib.rs:668-673).
    3. Runtime behavior confirms silent ignore for network inputs: pipeline wiring only passes max_open_files into TailConfig in the file branch (crates/logfwd/src/pipeline.rs:1282-1287).
    4. Existing validation pattern for “field only valid for specific type” already exists (request_mode is only supported for elasticsearch outputs, crates/logfwd-config/src/lib.rs:734-737), which is a good template for this fix.
    5. Docs currently list only path under file input fields and do not mention max_open_files (book/src/config/reference.md:83-90), so docs should be updated to avoid ambiguity.
    Verification

    I ran targeted CLI/config checks locally:

    $ cargo run -q -p logfwd -- --config (tcp-with-max_open_files).yaml --validate
    TCP_VALIDATE_EXIT=0
      ready: default
    config ok: 1 pipeline(s)
    
    $ cargo run -q -p logfwd -- --config (udp-with-max_open_files).yaml --validate
    UDP_VALIDATE_EXIT=0
      ready: default
    config ok: 1 pipeline(s)
    
    $ cargo run -q -p logfwd -- --validate --config (file-max_open_files-0).yaml
    FILE_ZERO_EXIT=1
    error: config validation error: pipeline 'default' input '#0': max_open_files must be at least 1
    
    $ RUSTC_WRAPPER= cargo test -q -p logfwd-config max_open_files -- --nocapture
    running 3 tests
    ...
    test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 54 filtered out
    Detailed Action Plan
    1. In crates/logfwd-config/src/lib.rs inside Config::validate() input loop (~638+), split max_open_files validation by input.input_type:
      • file: keep current == 0 rejection.
      • non-file: if max_open_files.is_some(), return validation error like:
        • pipeline '{name}' input '{label}': max_open_files is only supported for file inputs.
    2. Add config tests in crates/logfwd-config/src/lib.rs near existing max_open_files_* tests (:1695+):
    3. Keep existing positive file tests (max_open_files_one_accepted, absent accepted) unchanged.
    4. Add/adjust one integration-level validation case in crates/logfwd/tests/it/integration.rs near test_config_validation_errors (:382+) if maintainers want CLI-surface coverage beyond unit tests.
    5. Update book/src/config/reference.md under file input fields (:83-90) to explicitly document max_open_files as file-only with default 1024.
    Open PRs in this area

    Using local branch diff against fetched open PR heads:

    PR Status Overlap with relevant files
    #1003 (copilot/adopt-thiserror-2-0) Open Yes — touches crates/logfwd-config/src/lib.rs and crates/logfwd/src/pipeline.rs
    #1000 (copilot/convert-jsonlines-sink-to-reqwest) Open No overlap found with logfwd-config/input files above
    #1027 (copilot/perf-find-throughput-ceiling) Open No overlap found with logfwd-config/input files above
    Related Items
    Type Link Relevance
    Issue #1072 (open) Same bug class for non-file inputs (generator/otlp) accepting max_open_files
    Issue #696 (closed) Added current lower-bound validation (max_open_files >= 1) for file inputs
    Issue #193 (closed) Original feature introducing file tailer max_open_files behavior
    File crates/logfwd-config/src/lib.rs Validation rule should be tightened here
    File crates/logfwd/src/pipeline.rs Confirms max_open_files is file-branch-only at runtime
    File book/src/config/reference.md Needs explicit max_open_files file-only documentation

    Note

    🔒 Integrity filtering filtered 6 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