Skip to content

fix: parse components/headers to resolve crash on header component $ref in schemas#3676

Open
Copilot wants to merge 3 commits intomainfrom
copilot/fix-crash-report-issue
Open

fix: parse components/headers to resolve crash on header component $ref in schemas#3676
Copilot wants to merge 3 commits intomainfrom
copilot/fix-crash-report-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Specs that reference #/components/headers/{name} or #/components/responses/{name} from within a schema (e.g. a schema property) caused an unhandled crash: Symbol finalName has not been resolved yet for [Symbol {"category":"type","resource":"definition","resourceId":"#/components/headers/headers"}#130].

The parsers never processed components.headers or components.responses, so referenceSymbol() created an unresolvable stub with name: ''. During planning, assignLocalNames called setFinalName(''), then the finalName getter threw because "" is falsy.

Changes

New IR kinds: header and response

Instead of expanding the schema regex to match headers/responses, we introduce dedicated top-level IR kinds:

  • ir/graph.ts — add 'header' and 'response' to irTopLevelKinds, each with their own regex pattern in matchIrPointerToGroup, and positioned in preferGroups before schema
  • plugins/shared/types/instance.ts — add header and response variants to the WalkEvents union
  • plugins/shared/utils/instance.ts — add case 'header': and case 'response': to the forEach switch

Parsers

  • ir/types.ts — add headers?: Record<string, IRSchemaObject> and responses?: Record<string, IRResponseObject> to IRComponentsObject
  • 3.0.x/parser/schema.ts, 3.1.x/parser/schema.ts — add parseHeader() (extracts a header's schema) and parseResponse() (extracts a response's content schema). Converted to function syntax.
  • 3.0.x/parser/index.ts, 3.1.x/parser/index.ts — iterate components.headers and components.responses, calling the respective parse functions

Plugin updates

  • @hey-api/typescript v1 — subscribe to 'header' and 'response' events, processing their schemas through the same type processor as 'schema' events

Tests

  • Separate test specs for 3.0.x and 3.1.x (components-headers.yaml) covering both components/headers and components/responses
  • Specs follow the repo naming convention ("OpenAPI 3.x.x components headers and responses example")
  • Updated graph unit tests for the new header and response pointer patterns

Result

Given:

components:
  headers:
    X-Rate-Limit:
      schema:
        type: integer
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            type: object
            properties:
              code:
                type: integer
              message:
                type: string
  schemas:
    Foo:
      properties:
        rateLimit:
          $ref: '#/components/headers/X-Rate-Limit'

The generator now emits:

export type XRateLimit = number;

export type NotFound = {
    code?: number;
    message?: string;
};

export type Foo = {
    rateLimit?: XRateLimit;
};

instead of crashing. Header components without a schema property are silently skipped.

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hey-api-docs Ready Ready Preview, Comment Apr 3, 2026 1:20am

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 2, 2026

⚠️ No Changeset found

Latest commit: b7b47e9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

Copilot AI linked an issue Apr 2, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Fix crash caused by unresolved symbol in OpenAPI generation fix: parse components/headers to resolve crash on header component $ref in schemas Apr 2, 2026
Copilot AI requested a review from mrlubos April 2, 2026 20:54
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 0% with 48 lines in your changes missing coverage. Please review.
✅ Project coverage is 39.29%. Comparing base (549a42e) to head (b7b47e9).

Files with missing lines Patch % Lines
packages/shared/src/openApi/3.0.x/parser/schema.ts 0.00% 12 Missing and 6 partials ⚠️
packages/shared/src/openApi/3.1.x/parser/schema.ts 0.00% 12 Missing and 6 partials ⚠️
...pi-ts/src/plugins/@hey-api/typescript/v1/plugin.ts 0.00% 6 Missing ⚠️
...ckages/shared/src/plugins/shared/utils/instance.ts 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3676      +/-   ##
==========================================
- Coverage   39.39%   39.29%   -0.11%     
==========================================
  Files         520      520              
  Lines       19279    19323      +44     
  Branches     5708     5726      +18     
==========================================
- Hits         7595     7593       -2     
- Misses       9445     9479      +34     
- Partials     2239     2251      +12     
Flag Coverage Δ
unittests 39.29% <0.00%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 2, 2026

Open in StackBlitz

@hey-api/codegen-core

npm i https://pkg.pr.new/@hey-api/codegen-core@3676

@hey-api/json-schema-ref-parser

npm i https://pkg.pr.new/@hey-api/json-schema-ref-parser@3676

@hey-api/nuxt

npm i https://pkg.pr.new/@hey-api/nuxt@3676

@hey-api/openapi-ts

npm i https://pkg.pr.new/@hey-api/openapi-ts@3676

@hey-api/shared

npm i https://pkg.pr.new/@hey-api/shared@3676

@hey-api/spec-types

npm i https://pkg.pr.new/@hey-api/spec-types@3676

@hey-api/types

npm i https://pkg.pr.new/@hey-api/types@3676

@hey-api/vite-plugin

npm i https://pkg.pr.new/@hey-api/vite-plugin@3676

commit: b7b47e9

Copy link
Copy Markdown
Member

@mrlubos mrlubos left a comment

Choose a reason for hiding this comment

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

@copilot few notes:

  1. Make sure to add a handler and test for responses too. You can roll both responses and headers into one OpenAPI spec, that's fine.
  2. We need a separate test and test spec for OpenAPI 3.0 and OpenAPI 3.1, you added only tests for 3.0.
  3. The specs should follow the patterns from other specs (info object, field names and values) and be in YAML format (you're already doing this).
  4. Prefer function syntax over const where possible. We're migrating from const to function, so clean up the files as you go instead of creating more mess.
  5. This one is just a question for now, but why did you decide to expand the regular expression for schema pattern instead of adding a new entry for headers?

@mrlubos mrlubos marked this pull request as ready for review April 3, 2026 00:04
@pullfrog
Copy link
Copy Markdown

pullfrog bot commented Apr 3, 2026

Reviewed PR #3676. The approach of treating components/headers schemas as schema-kind IR nodes is correct and avoids invasive plugin changes. Left two comments: (1) a potential name collision when a header and schema share the same name (both derive identical base names via pathToName), and (2) a suggestion to expand the test spec to cover edge cases like same-name collisions and transitive $ref in header schemas.

Task list (6/6 completed)
  • Read the diff and identify major areas of change
  • Review core logic changes (ir/types.ts, ir/graph.ts)
  • Review parser changes (3.0.x and 3.1.x)
  • Review tests and snapshots
  • Verify correctness and check for edge cases
  • Submit review or report progress

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. bug 🔥 Broken or incorrect behavior. labels Apr 3, 2026
Copy link
Copy Markdown

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

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

The approach is sound — treating header schemas as schema-kind IR nodes avoids pervasive plugin changes and correctly leverages the existing graph walker infrastructure. Two issues worth addressing before merge.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run𝕏

parameter: /^#\/components\/parameters\/[^/]+$/,
requestBody: /^#\/components\/requestBodies\/[^/]+$/,
schema: /^#\/components\/schemas\/[^/]+$/,
schema: /^#\/components\/(headers|schemas)\/[^/]+$/,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Because pathToName's ROOT_CONTEXT for components skips 2 segments and collects 1, both #/components/headers/Foo and #/components/schemas/Foo derive the base name "Foo". The planner's assignSymbolName will silently rename one (e.g., Foo2), which is non-obvious for users.

This is an edge case — most real-world specs won't have colliding header and schema names — but it's worth calling out. Consider whether the ROOT_CONTEXT should be adjusted to include the component category in the name for headers (e.g., prefix with the header name), or at minimum add a test case to graph.test.ts that asserts #/components/headers/Foo matches 'schema' so the behaviour is documented.

type: object
properties:
rateLimit:
$ref: '#/components/headers/X-Rate-Limit'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The test spec only covers the happy path (header with a schema that is $ref'd from a schema property). Consider adding a case where a header and a schema share the same name to verify the collision-resolution behaviour, and a header whose schema is itself a $ref to another schema (to confirm schemaToIrSchema handles the transitive reference).

@mrlubos
Copy link
Copy Markdown
Member

mrlubos commented Apr 3, 2026

@pullfrog few notes:

  1. Make sure to add a handler and test for responses too. You can roll both responses and headers into one OpenAPI spec, that's fine.
  2. We need a separate test and test spec for OpenAPI 3.0 and OpenAPI 3.1, you added only tests for 3.0.
  3. The specs should follow the patterns from other specs (info object, field names and values) and be in YAML format (you're already doing this).
  4. Prefer function syntax over const where possible. We're migrating from const to function, so clean up the files as you go instead of creating more mess.
  5. I think we should add a new pattern for headers and responses instead of expanding the regular expression for schema pattern. Updating plugins is not a huge deal, the diff will be surgical. Plus, currently the plugins don't handle headers, so they won't lose any functionality by performing this change.

@pullfrog
Copy link
Copy Markdown

pullfrog bot commented Apr 3, 2026

Addressed all 5 review notes: added components/responses parsing alongside headers, introduced dedicated header and response IR kinds (instead of expanding the schema regex), created separate 3.0.x and 3.1.x test specs following repo conventions, converted touched functions to function syntax, and subscribed the TypeScript plugin to the new event types. All 2040 tests pass, typecheck and lint are clean.

Task list (12/12 completed)
  • Explore codebase patterns: existing specs, tests, graph, responses handling
  • Add separate header and response patterns in graph.ts + update irTopLevelKinds, preferGroups
  • Update WalkEvents union type in instance types
  • Update forEach switch in instance.ts
  • Update graph.test.ts with new patterns
  • Add responses to IRComponentsObject type
  • Add responses handler + parser (both 3.0.x and 3.1.x)
  • Convert const to function syntax in touched files
  • Create test specs for 3.0 and 3.1 with both headers and responses
  • Add tests for both 3.0.x and 3.1.x
  • Build, test, lint — update snapshots
  • Commit, push, report progress

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

…s`, add 3.1.x test

- Add `header` and `response` as separate top-level IR kinds instead of
  expanding the `schema` regex pattern
- Parse `components/responses` in both 3.0.x and 3.1.x parsers
- Add `responses` to `IRComponentsObject` type
- Update `WalkEvents` union and `forEach` switch for new event types
- Subscribe `@hey-api/typescript` plugin to `header` and `response` events
- Create `components-headers.yaml` spec for 3.1.x (with both headers
  and responses)
- Update 3.0.x spec to follow naming conventions and include responses
- Convert `parseSchema`/`parseHeader` to `function` syntax
- Update graph unit tests for new patterns
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug 🔥 Broken or incorrect behavior. size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash Report

2 participants