Skip to content

feat(internal): filter optional header parameters from dynamic snippet generation#12287

Open
tjb9dc wants to merge 7 commits intomainfrom
devin/1770918592-filter-optional-headers-snippets
Open

feat(internal): filter optional header parameters from dynamic snippet generation#12287
tjb9dc wants to merge 7 commits intomainfrom
devin/1770918592-filter-optional-headers-snippets

Conversation

@tjb9dc
Copy link
Collaborator

@tjb9dc tjb9dc commented Feb 12, 2026

Description

Refs: Requested by @tjb9dc

Excludes optional/nullable header parameters from generated code snippets across all language generators. Previously, all headers (including optional ones) were included in dynamic snippets, which cluttered the generated examples with parameters that aren't required.

Link to Devin run

Changes Made

  • Added filterRequiredParameters() helper to AbstractDynamicSnippetsGeneratorContext that filters out parameters where isOptional() or isNullable() returns true on the type reference
  • Updated needsRequestParameter() in the abstract context to only consider required headers when deciding if a request wrapper is needed
  • Applied the filter at constructor/global headers (ir.headers) in each of the 8 language generators
  • For per-request headers (request.headers): pass full headers list with ignoreMissingParameters: true to allow user-provided optional header values to be included while avoiding lookup failures
  • Generators updated: TypeScript, Python, Java (2 constructor sites), Go, C#, Ruby, PHP, Rust
  • Swift generator was not modified (it has no header processing logic)
  • Fixed 5 pre-existing TS compile errors in AbstractDynamicSnippetsGeneratorContext.ts by adding return before assertNever() calls (surfaced because turbo cache was invalidated by the main changes)

Updates Since Last Revision

Addressed review comment (#discussion_r2800466633):

  • Reverted filterRequiredParameters at per-request associateByWireValue call sites across all 8 generators
  • Now passing full request.headers ?? [] with ignoreMissingParameters: true instead
  • This fixes a bug where filtering parameters before associateByWireValue caused false "not recognized" critical errors when users provided values for optional headers
  • Constructor header filtering remains unchanged (safe because it iterates over parameters, not user values)

Additionally:

  • Fixed 5 pre-existing assertNever TS2366/TS7030 compile errors in AbstractDynamicSnippetsGeneratorContext.ts (isFileUploadRequestBody, resolveEnvironmentName, includeRequestBodyInWrappedRequest, fileUploadHasBodyProperties, fileUploadHasFileProperties) by adding return before assertNever() — these were exposed when the turbo cache was invalidated by changes to the same file

Review Checklist

  • Filtering semantics: The filter excludes both optional AND nullable type references. Verify this is the desired behavior — a header that is nullable but technically "required" will also be excluded. See filterRequiredParameters() in AbstractDynamicSnippetsGeneratorContext.ts.
  • ignoreMissingParameters behavior: Per-request headers now use ignoreMissingParameters: true, which silently skips user-provided header values that don't match any parameter. This could mask typos in header names.
  • needsRequestParameter() change: Endpoints with only optional headers won't generate request wrappers. Verify this is acceptable.
  • No test fixtures added: Relying on existing seed tests to validate. Consider whether a dedicated fixture with optional headers would be valuable.

Testing

  • Lint passes (pnpm run check)
  • Local compile verified (pnpm turbo run compile --filter=@fern-api/browser-compatible-base-generator --force and downstream @fern-api/go-formatter)
  • All 14 required CI checks pass
  • Non-required seed test failures (csharp-sdk, java-model, fastapi) are pre-existing TS2366 errors in logger/src/console.ts, ir-utils, etc. on main — unrelated to this PR
  • Unit tests added/updated — no new tests; relying on existing seed test infrastructure to validate

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Contributor

🌱 Seed Test Selector

Select languages to run seed tests for:

  • Python
  • TypeScript
  • Java
  • Go
  • Ruby
  • C#
  • PHP
  • Swift
  • Rust
  • OpenAPI
  • Postman

How to use: Click the ⋯ menu above → "Edit" → check the boxes you want → click "Update comment". Tests will run automatically and snapshots will be committed to this PR.

@devin-ai-integration devin-ai-integration bot changed the title feat: filter optional header parameters from dynamic snippet generation feat(internal): filter optional header parameters from dynamic snippet generation Feb 12, 2026
Copy link
Collaborator Author

@tjb9dc tjb9dc left a comment

Choose a reason for hiding this comment

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

Looks good! But we also need new changelogs for all the SDKs (in versions.yml). These should all be type fix and be a PATCH version bump. For the summary just put Filter out optional headers when generating dynamic snippets.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

this.context.errors.scope(Scope.Headers);
const headers = this.context.associateByWireValue({
parameters: request.headers ?? [],
parameters: this.context.filterRequiredParameters(request.headers ?? []),
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 Filtering parameters before associateByWireValue causes false "not recognized" critical errors for optional headers

When a user provides values for optional/nullable headers in snippet.headers, the new filterRequiredParameters call removes those headers from the parameters list before passing it to associateByWireValue. However, associateByWireValue iterates over the user-provided values (not the parameters), and for each value, it looks up the corresponding parameter by wire value (parameters.find(...) at AbstractDynamicSnippetsGeneratorContext.ts:126). When an optional header's value is present in values but its parameter definition has been filtered out, the lookup fails and a Severity.Critical error is emitted: "<headerName>" is not a recognized parameter for this endpoint.

Root Cause and Affected Files

The associateByWireValue method at generators/browser-compatible-base/src/dynamic-snippets/AbstractDynamicSnippetsGeneratorContext.ts:110-149 iterates over Object.entries(values) and tries to find each key in the parameters array. When the parameter is not found (because it was filtered out as optional), it adds a Severity.Critical error.

This same bug pattern is repeated in all generators that pass filtered parameters to associateByWireValue for inlined request headers:

  • generators/csharp/dynamic-snippets/src/EndpointSnippetGenerator.ts:600
  • generators/go-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts:754
  • generators/java-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts:822
  • generators/php/dynamic-snippets/src/EndpointSnippetGenerator.ts:865
  • generators/python-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts:871
  • generators/rust/dynamic-snippets/src/EndpointSnippetGenerator.ts:1049

Impact: Any snippet request that includes values for optional or nullable headers will produce spurious critical errors and drop those header values from the generated snippet. This could cause snippet generation to be reported as failed for perfectly valid input.

Prompt for agents
In all generators (TypeScript, C#, Go, Java, PHP, Python, Rust), when calling associateByWireValue for inlined request headers, do NOT pass filtered parameters. Instead, pass the full request.headers list to associateByWireValue so that user-provided values for optional headers are matched correctly (and simply produce no output when the value is null/undefined). The filterRequiredParameters call should only be used in needsRequestParameter (to decide whether a request wrapper is needed at all) and in the constructor header args (which iterate over parameters, not values, so filtering is safe there). Alternatively, if the intent is to exclude optional headers from the generated snippet entirely, pass ignoreMissingParameters: true to associateByWireValue so that values for optional headers are silently skipped instead of producing critical errors.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch. Fixed in 4e7517d — reverted filterRequiredParameters at all associateByWireValue per-request header call sites across all 7 generators. Now passing the full headers list with ignoreMissingParameters: true instead, so optional header values provided by users are silently skipped rather than producing critical errors.

The constructor header filtering and needsRequestParameter filtering remain unchanged (those iterate over parameters, not user values, so filtering is safe there).

// Create RequestOptions with additional headers
const headers = this.context.associateByWireValue({
parameters: request.headers ?? [],
parameters: this.context.filterRequiredParameters(request.headers ?? []),
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 Filtering parameters before associateByWireValue causes false critical errors in Rust getRequestOptionsWithHeaders

Same root cause as BUG-0001 but in the Rust generator's getRequestOptionsWithHeaders method. When a user provides values for optional/nullable headers, the filtered parameters list passed to associateByWireValue causes those values to be reported as unrecognized.

Detailed Explanation

At line 1049, this.context.filterRequiredParameters(request.headers ?? []) removes optional headers from the parameters list. But associateByWireValue at AbstractDynamicSnippetsGeneratorContext.ts:120 iterates over Object.entries(values) (the user-provided snippet.headers). If the user provided a value for an optional header, that header's parameter won't be found in the filtered list, and a Severity.Critical error will be added.

Additionally, the hasHeaders check at line 652 only considers required headers, so if the user provides values exclusively for optional headers, hasHeaders will be false, and those values will be silently dropped with no RequestOptions generated at all.

Impact: Valid snippet requests with optional header values produce spurious critical errors and missing header output in generated Rust snippets.

Suggested change
parameters: this.context.filterRequiredParameters(request.headers ?? []),
parameters: request.headers ?? [],
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

devin-ai-integration bot and others added 6 commits February 12, 2026 18:37
Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
…es for per-request headers

Pass full headers list with ignoreMissingParameters: true instead of
filtering, to avoid false critical errors when users provide values for
optional headers.

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
…ets (resolve python versions.yml conflict)

Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments