Skip to content

fix: do not run nested visitors with a stale parent on shared $refs - #3038

Open
vadyvas wants to merge 3 commits into
mainfrom
fix/walker-stale-parent-shared-refs
Open

fix: do not run nested visitors with a stale parent on shared $refs#3038
vadyvas wants to merge 3 commits into
mainfrom
fix/walker-stale-parent-shared-refs

Conversation

@vadyvas

@vadyvas vadyvas commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What/Why/How?

operation-parameters-unique reported a duplicate parameter that is not there. It happens when the same path item is reused through $ref, for example from two callbacks, and the reported location points at an operation that has no parameters at all.

The reason is in the walker. Two $refs to the same path item resolve to one object, so the walker enters it twice. On the second entry it does not call the rule's enter hooks again, because it already called them for that node — but it still calls the rule's nested Parameter visitor. So the visitor runs with the state and the parent location left over from the first entry, and reports a parameter it had already counted.

Added a guard: a nested visitor now runs only if the node its parent hook sits on is the node the walker is really inside. On the second entry the Parameter visitor stays quiet, and the rule reports only what it actually saw. operation-parameters-unique is not the only rule with this shape — spec-querystring-parameters had the same problem.

Reference

Closes #2829

Testing

Screenshots (optional)

x-signature is declared once, in one list:

paths:
  /sample:
    get:                                  # no parameters here
      callbacks:
        onEventA:
          'https://callbacks.example.com/a':
            $ref: '#/components/pathItems/notify'
        onEventB:
          'https://callbacks.example.com/b':
            $ref: '#/components/pathItems/notify'
components:
  pathItems:
    notify:
      post:
        parameters:
          - in: header
            name: x-signature            # declared once, used in both callbacks

Before:

validating openapi.yaml using lint rules for api 'main'...
[1] openapi.yaml:9:7 at #/paths/~1sample/get/parameters/0

Operations must have unique `name` + `in` parameters. Repeats of `in:header` + `name:x-signature`.

 7 |   /sample:
 8 |     get:
 9 |       operationId: getSample
   |       ^^^^^^^^^^^^^^^^^^^^^^
10 |       summary: example summary
   |       ^^^^^^^^^^^^^^^^^^^^^^^^

Error was generated by the operation-parameters-unique rule.

❌ Validation failed with 1 error.

After:

validating openapi.yaml using lint rules for api 'main'...
openapi.yaml: validated in 4ms

Woohoo! Your API description is valid. 🎉

Check yourself

  • This PR follows the contributing guide
  • All new/updated code is covered by tests
  • Core code changed? - Tested with other Redocly products (internal contributions only)
  • New package installed? - Tested in different environments (browser/node)
  • Documentation update has been considered

Security

  • The security impact of the change has been considered
  • Code follows company security practices and guidelines

Note

Medium Risk
Touches core lint traversal used by all nested rules; behavior change is narrow and well-tested but could affect edge cases in custom rules relying on the old (buggy) re-entry semantics.

Overview
Fixes false duplicate-parameter lint errors when the same path item is referenced from multiple $refs (e.g. two callbacks pointing at #/components/pathItems/notify). Multiple references resolve to one object, so the walker could re-enter that node without re-running parent enter hooks while still firing nested visitors—leaving stale parent context and bogus reports on unrelated operations.

The OpenAPI walker in walk.ts now tracks the node actually being walked per type (walkingNodeByType) and only activates nested visitors when the parent’s activated node matches that stack. Sibling keys beside a $ref still get walked as before.

Adds unit coverage for shared callback path items and $ref siblings, plus an e2e fixture where operation-parameters-unique and path-parameters-defined must pass when a shared path item is reused.

Reviewed by Cursor Bugbot for commit 394cd16. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 394cd16

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@redocly/cli Patch
@redocly/openapi-core Patch
@redocly/client-generator Patch
@redocly/respect-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vadyvas

vadyvas commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 77.33% (🎯 77%) 11675 / 15096
🔵 Statements 77.43% (🎯 77%) 12499 / 16142
🔵 Functions 81.43% (🎯 81%) 2355 / 2892
🔵 Branches 71% (🎯 71%) 8587 / 12093
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/core/src/walk.ts 98.27% 97.46% 85% 98.77% 222, 237, 467
Generated in workflow #11297 for commit b4d4615 by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Performance Benchmark (Lower is Faster)

CLI Version Bundle Lint Check Config
cli-latest ▓ 1.01x ± 0.01 ▓ 1.00x ± 0.01 ▓ 1.00x (Fastest)
cli-next ▓ 1.00x (Fastest) ▓ 1.00x (Fastest) ▓ 1.01x ± 0.01

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5467388. Configure here.

@vadyvas
vadyvas marked this pull request as ready for review August 11, 2026 16:15
@vadyvas
vadyvas requested review from a team as code owners August 11, 2026 16:15
@vadyvas vadyvas self-assigned this Aug 11, 2026
Comment thread .changeset/quiet-bears-allow.md Outdated
Comment thread packages/core/src/walk.ts
}
}

walkingNodeByType[type.name] = prevWalkingNode;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why reassigning it back?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this keeps track of the node we are currently inside
without the restore, after we leave a child and move to a sibling, the map would still say we are inside that child and the check below would wrongly skip visitors

@tatomyr

tatomyr commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Please make sure to test the snapshot:
image

@vadyvas
vadyvas force-pushed the fix/walker-stale-parent-shared-refs branch from b4d4615 to 394cd16 Compare August 26, 2026 15:36
@vadyvas vadyvas added snapshot Create experimental release PR and removed snapshot Create experimental release PR labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 A new experimental 🧪 version v0.0.0-snapshot.1787819613 of Redocly CLI has been published for testing.

Install with NPM:

npm install @redocly/cli@0.0.0-snapshot.1787819613

⚠️ Note: This is a development build and may contain unstable features.

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

Labels

snapshot Create experimental release PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linter reports duplicate parameters when multi-referencing path items

3 participants