feat(internal): filter optional header parameters from dynamic snippet generation#12287
feat(internal): filter optional header parameters from dynamic snippet generation#12287
Conversation
Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
🌱 Seed Test SelectorSelect languages to run seed tests for:
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. |
tjb9dc
left a comment
There was a problem hiding this comment.
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.
| this.context.errors.scope(Scope.Headers); | ||
| const headers = this.context.associateByWireValue({ | ||
| parameters: request.headers ?? [], | ||
| parameters: this.context.filterRequiredParameters(request.headers ?? []), |
There was a problem hiding this comment.
🔴 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:600generators/go-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts:754generators/java-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts:822generators/php/dynamic-snippets/src/EndpointSnippetGenerator.ts:865generators/python-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts:871generators/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.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
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 ?? []), |
There was a problem hiding this comment.
🔴 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.
| parameters: this.context.filterRequiredParameters(request.headers ?? []), | |
| parameters: request.headers ?? [], |
Was this helpful? React with 👍 or 👎 to provide feedback.
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>
…er-optional-headers-snippets
Co-Authored-By: thomas@buildwithfern.com <tjb9dcshop@gmail.com>
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
filterRequiredParameters()helper toAbstractDynamicSnippetsGeneratorContextthat filters out parameters whereisOptional()orisNullable()returns true on the type referenceneedsRequestParameter()in the abstract context to only consider required headers when deciding if a request wrapper is neededir.headers) in each of the 8 language generatorsrequest.headers): pass full headers list withignoreMissingParameters: trueto allow user-provided optional header values to be included while avoiding lookup failuresAbstractDynamicSnippetsGeneratorContext.tsby addingreturnbeforeassertNever()calls (surfaced because turbo cache was invalidated by the main changes)Updates Since Last Revision
Addressed review comment (#discussion_r2800466633):
filterRequiredParametersat per-requestassociateByWireValuecall sites across all 8 generatorsrequest.headers ?? []withignoreMissingParameters: trueinsteadassociateByWireValuecaused false "not recognized" critical errors when users provided values for optional headersAdditionally:
assertNeverTS2366/TS7030 compile errors inAbstractDynamicSnippetsGeneratorContext.ts(isFileUploadRequestBody,resolveEnvironmentName,includeRequestBodyInWrappedRequest,fileUploadHasBodyProperties,fileUploadHasFileProperties) by addingreturnbeforeassertNever()— these were exposed when the turbo cache was invalidated by changes to the same fileReview Checklist
optionalANDnullabletype references. Verify this is the desired behavior — a header that is nullable but technically "required" will also be excluded. SeefilterRequiredParameters()inAbstractDynamicSnippetsGeneratorContext.ts.ignoreMissingParametersbehavior: Per-request headers now useignoreMissingParameters: 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.Testing
pnpm run check)pnpm turbo run compile --filter=@fern-api/browser-compatible-base-generator --forceand downstream@fern-api/go-formatter)csharp-sdk,java-model,fastapi) are pre-existingTS2366errors inlogger/src/console.ts,ir-utils, etc. onmain— unrelated to this PR