fix(core): skip discriminator mapping entries whose target schema is absent - #3551
Conversation
…absent resolveDiscriminators looked up each discriminator.mapping target in the schema set and dereferenced .properties on it. When the target was missing (e.g. a subtype removed by filters.tags) it threw "Cannot read properties of undefined (reading 'properties')". Skip undefined targets instead of dereferencing them.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughSkip dereferencing missing discriminator mapping targets during subtype augmentation; add a Vitest regression test confirming pruned mapping entries are ignored and only existing subtype(s) receive the discriminator property with the correct enum. ChangesDiscriminator handling for missing subtype schemas
Sequence Diagram(s)sequenceDiagram
participant resolveDiscriminators
participant discriminatorMapping
participant subTypeSchema
resolveDiscriminators->>discriminatorMapping: iterate mapping entries (name -> ref)
discriminatorMapping->>subTypeSchema: attempt to resolve target schema
alt subTypeSchema is defined and valid
resolveDiscriminators->>subTypeSchema: add discriminator property with enum [key]
else subTypeSchema is undefined or boolean or propertyName missing
resolveDiscriminators-->>subTypeSchema: skip augmentation
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Prevents resolveDiscriminators from throwing when a discriminator mapping points to a schema that isn’t present (e.g., removed by tag filtering), and adds a regression test for that scenario.
Changes:
- Skip discriminator mapping entries when the target subtype schema is missing.
- Add a test ensuring missing mapping targets don’t throw and remaining subtypes are still augmented.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/core/src/getters/discriminators.ts | Adds a guard to skip missing mapped subtype schemas before dereferencing. |
| packages/core/src/getters/discriminators.test.ts | Adds coverage for absent mapping targets and verifies discriminator augmentation still occurs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c2f406b to
adc84aa
Compare
A throw would already fail the test, so the separate .not.toThrow() call is redundant. Call resolveDiscriminators once and assert on the result.
melloware
left a comment
There was a problem hiding this comment.
Looks like its failing Lint
Indexing the schema set with a mapping target that was filtered out (e.g. by `filters.tags`) or absent in a malformed spec returns `undefined` at runtime. Guard with `!subTypeSchema` before dereferencing, mirroring the existing `!variantSchema` check in the same function, and drop a redundant type assertion in the test.
Close #3550
Problem
When
input.filters.tagsis used and a surviving schema is a discriminator base (hasdiscriminator.mapping), generation crashes:It fails during schema generation, before any client code is emitted, so it is not client-specific — every client/
httpClientcombination fails identically.Root cause
filters.tagsprunes schemas not reachable via a real$reffrom a matching operation. Adiscriminator.mappingvalue is a bare string ('#/components/schemas/Foo'), not a$refobject, socollectReferencedComponentscorrectly does not follow it (following it would re-pull most of the spec and defeat filtering). The kept base therefore retains its fulldiscriminator.mapping, which now lists subtypes that were just pruned.resolveDiscriminatorsiterates that mapping, looks each target up in the filtered schema set, and the existing guard only checkedisBoolean(subTypeSchema)— notsubTypeSchema === undefined— so it dereferenced.propertieson the first missing target.Fix
Make
resolveDiscriminatorsskip a mapping entry whose target is absent (subTypeSchema === undefined) instead of dereferencing it. The kept base is emitted, surviving subtypes are generated with their discriminant, and pruned subtypes are simply ignored.This also hardens against malformed specs that reference a non-existent subtype.
Why resolver-side only
The issue suggests two composable fixes: (1) trim pruned entries from kept bases' mappings on the filter side, and (2) guard the resolver. In the end I went with (2) only, deliberately:
discriminator.mappinghas exactly one consumer that dereferences the target schema —resolveDiscriminators. This guard makes it safe. Other consumers (corecombine,mockvalue/combine) read the mapping only as strings and never chase a target, so a leftover entry is inert. The mapping is never emittedinto generated code.
Testing
discriminators.test.ts: a mapping with an absent target no longer throws, and the surviving subtype is still augmented with its discriminant.filters.tags: ['cats'], prunesDog): generates cleanly;Pet+Catemitted withCat.petTypeconstrained,Dogabsent.tsc --strict.Summary by CodeRabbit
Bug Fixes
Tests