Fix: Restore full error.details handling for binary wrap-non-model-return deserializers#3852
Fix: Restore full error.details handling for binary wrap-non-model-return deserializers#3852joheredi wants to merge 9 commits intoAzure:mainfrom
Conversation
…turn deserializers The wrap-non-model-return feature (commit 49500ef) introduced early returns in the binary wrap path that skipped status checking and error.details extraction in generated deserialize functions. This fix: - Adds a new getBinaryStream static helper that separates stream extraction from error handling (Node + browser variants) - Removes the status check skip for binary wrap - all operations now generate the full getExceptionThrowStatement with error.details, exception headers, and restErrorCodeHeader support - The deserializer parameter is renamed to _streamableResult for binary wrap, with getBinaryStream resolving it into an HttpResponse named result so existing error handling code references work unchanged Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds two new unit test scenarios verifying that binary wrap deserializers generate full error.details handling: - Binary wrap + error model: verifies error.details = apiErrorDeserializer(result.body) - Binary wrap + error model + exception headers: verifies error.details with exception header merging via _getBlobDeserializeExceptionHeaders(result) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Verifies that when enable-storage-compat and include-headers-in-response are both enabled on a void-body operation with response headers and an error model: 1. Deserializer generates error.details = storageErrorDeserializer(result.body) 2. Exception headers are merged into error.details 3. Operation function extracts parsedHeaders and passes them to addStorageCompatResponse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| * Error handling is left to the caller so that generated deserializers can apply | ||
| * operation-specific error deserialization (per-status-code details, exception headers, etc.). | ||
| */ | ||
| export async function getBinaryStream( |
There was a problem hiding this comment.
why not remove the exsiting two helpers?
- https://github.com/joheredi/autorest.typescript/blob/2ee958a5cbbaaa9e94e26e29f4161e11f11245ac/packages/typespec-ts/static/static-helpers/serialization/get-binary-response-body.ts
- https://github.com/joheredi/autorest.typescript/blob/2ee958a5cbbaaa9e94e26e29f4161e11f11245ac/packages/typespec-ts/static/static-helpers/serialization/get-binary-response-body-browser.mts
|
I tried 8fcaf1f build for storage-blob generation. There are compilation errors:
|
|
we can probably get away from casting |
ah not really. Previously I was relying on |
|
So currently |
|
I pushed a branch to show the issue and my manual fix. Not sure if it is easy to update the code gen to have similar fix, when the operation is returning stream body AND storage compat option is enabled.
my manual fix includes
|
| * Error handling is left to the caller so that generated deserializers can apply | ||
| * operation-specific error deserialization (per-status-code details, exception headers, etc.). | ||
| */ | ||
| export async function getBinaryStream(streamableMethod: StreamableMethod): Promise< |
There was a problem hiding this comment.
getBinaryStreamResponse a better name because response is also returned?
| export async function getBinaryStream(streamableMethod: StreamableMethod): Promise< | |
| export async function getBinaryStreamResponse(streamableMethod: StreamableMethod): Promise< |
- move the `getBinaryStream()` call out of `_operationDeserialize()` and into the operation method `operation()`, where its result is passed to both `_opDeserialize()` and `_operationDeserializeHeaders()` - `_operationDeserialize()` now takes the result of `getBinaryStream()`, process and throw for error response if any; return the wrapped stream body. - return type is fixed for wrapped operation.


Problem
Commit
49500efaaintroduced thewrap-non-model-returnfeature which defaults totruefor all Azure-flavored clients. The binary wrap path had two regressions:error.detailsextraction —if (!(shouldWrap && isBinary))completely bypassedgetExceptionThrowStatement(), the only function that generates per-status-codeerror.details, exception headers,@clientOptionheaders, andrestErrorCodeHeaderassignment.getBinaryResponse/parsedBody/parsedHeaders— an early return skipped all normal response processing.Root Cause
The binary wrap feature delegated everything to a
getBinaryResponseBodystatic helper that only accepted a singledeserializeErrorcallback, losing all rich error handling thatgetExceptionThrowStatement()provides.Fix (Approach: Split helper)
Created a new
getBinaryStreamstatic helper that only handles platform-specific stream extraction (Node:asNodeStream(), Browser:asBrowserStream()), then let the generated deserializer handle error checking using the samegetExceptionThrowStatementcode path as all other operations.Changes
New files:
static/static-helpers/serialization/get-binary-stream.ts— Node variantstatic/static-helpers/serialization/get-binary-stream-browser.mts— Browser variantModified files:
src/modular/static-helpers-metadata.ts— RegisteredgetBinaryStreamhelpersrc/modular/helpers/operationHelpers.ts:if (!(shouldWrap && isBinary))guard that skipped the status check_streamableResultfor binary wrap, withconst result = await getBinaryStream(_streamableResult)resolving it into anHttpResponseso existing error handling code works unchangedgetBinaryResponseBodycall with simple return of{ blobBody, readableStreamBody }from the resolved resulttest/modularUnit/scenarios/operations/wrapNonModelReturn.md— Updated binary scenario + added error enrichment scenariostest/modularUnit/scenarios/operations/storageCompat/storageCompatResponse.md— Added void+headers+error enrichment scenarioGenerated code before vs after
Before (no error handling):
After (full error handling):
Validation
pnpm build— successnpm run unit-test— 585 passing, 2 pendingpnpm format— cleannpm run lint— clean