Skip to content

6.15.2 parse behavioral change breaks nested bracket query string parsing #558

Closed

Description

@Israel-Kli

Summary

Upgrading from qs@6.15.0 to qs@6.15.2 introduced a behavioral change in parse that breaks existing query string handling in Express-based applications using nested bracket group notation.

Context

The 6.15.2 changelog mentions:

[Fix] parse: handle nested bracket groups and add regression tests (#530)

While this is framed as a fix, it changes the parsed output for query strings that were previously handled differently. Applications relying on the prior behavior see regressions without any code change on their end.

Impact

In our case, query strings like:

groupBy=service&groupBy=linkedaccid&startDate=2021-08-01&...

are parsed differently between 6.15.0 and 6.15.2, causing API endpoints to receive incorrect or empty parameter values, resulting in empty responses where data is expected.

Suggestion

It would help to:

  1. Clearly document this as a breaking behavioral change in the changelog (not just a fix), so consumers can assess impact before upgrading.
  2. Consider whether the prior behavior was actually incorrect or just undocumented, as many applications may depend on it.

Environment

  • qs version: 6.15.0 (working) → 6.15.2 (broken)
  • Used via npm overrides in an Express.js application
  • Node.js version: 24

Activity

  1. darekg11 commented on May 26, 2026

    @darekg11

    We are seeing similar behavior after upgrading from 6.15.1 to 6.15.2
    I think this fix accidentally slipped a breaking change

  2. ljharb commented on May 26, 2026

    @ljharb
    Owner

    Can you provide a full query string, and the output from both 6.15.1 and 6.15.2?

  3. darekg11 commented on May 27, 2026

    @darekg11

    Okay, in our case I think it was the fact that brackets parsing was more relaxed in 6.15.1 and what you did is intentional.

    const parsed = qs.parse("filters[metadata.imported][conditions][$is][[0]]=import-123", { arrayLimit: 1000 });
    
    OUTPUT - 6.15.1: {"filters":{"metadata.imported":{"conditions":{"$is":["import-123"]}}}}
    
    OUTPUT - 6.15.2: {"filters":{"metadata.imported":{"conditions":{"$is":{"[0]":"import-123"}}}}}
    

    I guess my comment is irrelevant since it was intended change and we should update our code to not do such constructs of nested arrays when not needed

  4. Israel-Kli commented on May 31, 2026

    @Israel-Kli
    Author

    We also hit this. Minimal reproduction:

    Query string: groupBy=service&filters[customtags:Env: Prod&startDate=2025-02-01

    The key filters[customtags:Env: Prod is malformed — it has an opening [ but no closing ] (a typo in our code; the : should have been ]=). Even so, the parse output changed between versions:

    6.15.0 treats it as a flat key (lenient — unmatched [ ignored):

    {
      "groupBy": "service",
      "filters[customtags:Env: Prod": "",
      "startDate": "2025-02-01"
    }

    6.15.2 interprets filters[ as starting a nested bracket group despite the missing ]:

    {
      "groupBy": "service",
      "filters": {
        "[customtags:Env: Prod": ""
      },
      "startDate": "2025-02-01"
    }

    Yes, our query string is malformed, and fixing it on our side is the right long-term answer. But this is a real behavior change for any app that previously relied on the lenient flat-key fallback for unbalanced brackets. Worth a CHANGELOG callout at minimum.

  5. ljharb commented on Jun 2, 2026

    @ljharb
    Owner

    @Israel-Kli thanks - tbh i think the thing it should do there is throw.

  6. Israel-Kli commented on Jun 8, 2026

    @Israel-Kli
    Author

    Closing the loop here: in our case the only failing input was a Newman test typo on our side — filters[customtags:Env: Prod (unclosed [). Diff harness across 10 well-formed shapes from our integration tests + prod traffic vs that 1 malformed case shows only the malformed one differs between 6.15.0 and 6.15.2. So 6.15.2 is safe for us after fixing the typo; the original regression report was a misdiagnosis. Thanks @ljharb for the patient back-and-forth — agreeing 6.15.2's behavior is the right one. Happy for this to be closed (or kept open for your "throw on unbalanced brackets" follow-up, your call).

  7. ljharb commented on Jun 10, 2026

    @ljharb
    Owner

    I'll leave this open until til #560 closes it. Thanks for following up!

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions