[PR #196] fix(models): raise the typed error when a non-2xx body read fails and no retry remains #198

Open
opened 2026-10-31 22:05:35 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/comfy-typescript-sdk/pull/196
Author: @mattmillerai
Created: 9/29/2026
Status: 🔄 Open

Base: main ← Head: mattmiller/be-17634-typed-error-on-unread-body


📝 Commits (8)

  • df14fa2 fix(models): raise the typed error when a non-2xx body read fails and no retry remains
  • 268799d fix(models): own-property bucket lookup, and a non-enumerable queue cause
  • c0f95bd fix(errors): own-property code lookup in the low layer, and an honest message for a cut-off queue 422
  • 53aec07 fix(errors): own-property lookup in toSdkError, a string-only envelope code, and a typed error for a cut-off discovery body
  • 830cb26 fix(errors): string-only envelope message, retryAfter on every sdk error, and a non-throwing validation summary
  • 6ec46e7 Merge remote-tracking branch 'origin/main' into mattmiller/be-17634-typed-error-on-unread-body
  • f9b5a51 Merge remote-tracking branch 'origin/main' into mattmiller/be-17634-typed-error-on-unread-body
  • c7413f2 Merge remote-tracking branch 'origin/main' into mattmiller/be-17634-typed-error-on-unread-body

📊 Changes

12 files changed (+444 additions, -14 deletions)

View changed files

📝 CHANGELOG.md (+23 -0)
📝 src/low/errors.test.ts (+32 -0)
📝 src/low/errors.ts (+12 -3)
📝 src/sdk/exceptions.test.ts (+20 -0)
📝 src/sdk/exceptions.ts (+4 -1)
📝 src/sdk/modelRequests.test.ts (+62 -0)
📝 src/sdk/modelRequests.ts (+36 -1)
📝 src/sdk/models-discovery.test.ts (+28 -0)
📝 src/sdk/models.test.ts (+130 -0)
📝 src/sdk/models.ts (+55 -5)
📝 src/sdk/routerErrors.ts (+7 -2)
📝 test/support/router-stub-server.ts (+35 -2)

📄 Description

ELI-5

When Router answers with an error (say a 422 or a 402) and the connection drops while the SDK is still reading the error's body, the SDK used to give up with a bare TypeError: terminated — even though the status line and headers had already told it exactly what went wrong. Now, once retries are used up, it throws the proper typed error (InsufficientCredits, InvalidInput, …) built from that status and those headers, and keeps the network failure as cause.

What changed

  • src/sdk/models.ts (models.run) — the retry loop remembers a non-2xx response whose readBodyWithin call failed (unreadError). Only that read is wrapped; fetch and the classification are not. In the outer catch the order stays as it was (isTooLarge → isTimeout → caller abort → nextDelayMs()). Only when nextDelayMs() returns null and unreadError is set does it throw errorFromResponse(unreadError, "", idempotencyKey, exc). Because that check comes after the three early exits, a cap breach, a deadline or a caller abort is never re-dressed. errorFromResponse gets an optional 4th cause parameter, passed into the options only when it is provided, so existing call sites are unchanged. It now looks up the bucket class with Object.hasOwn, like toRouterError already did, because on this path the header is the only input and a constructor/toString header must not resolve off Object.prototype.
  • src/sdk/modelRequests.ts (send()) — the same pattern around response.text(): toRouterError(status, headers, null), with cause set through Object.defineProperty as a non-enumerable property. That is the shape ES2022's new Error(msg, { cause }) gives the models.run twin. toRouterError's signature and the public RouterErrorOptions are unchanged.
  • test/support/router-stub-server.ts — a new cutBodyTimes knob. It sends the ordinary status and headers with no Content-Length, writes {"detail":[, then destroys the socket. This sits between resetTimes (no status line at all) and stallBody (never drops). ScriptedResponse also gets a cutBody flag, so one scripted route can drop its body, and the respond doc now lists cutBodyTimes among the checks that run before it.
  • src/low/errors.ts (errorFromEnvelope) — review round 2. The Comfy API v2 code-to-class lookup now uses Object.hasOwn(BY_CODE, code) too. Before this, an envelope code of constructor or toString resolved off Object.prototype.
  • Queue path message — review round 2. On the cut-off path, send() passes toRouterError a string detail of HTTP <status>. A 422 whose body was never read now reads HTTP 422, like the models.run twin, rather than InvalidInput's summary of an empty detail[]. The InvalidInput.detail doc in src/sdk/routerErrors.ts now covers the unread-body case, where cause is set.
  • Review round 3 (53aec07).
    • src/sdk/exceptions.ts (toSdkError): the sdk-layer code-to-class lookup now uses Object.hasOwn(BY_CODE, exc.code). Before, the ApiError that errorFromEnvelope built safely was looked up by prototype chain again one hop later.
    • src/low/errors.ts: a code that is not a string is treated as absent, and the status-derived code is used instead.
    • src/sdk/models.ts (discoveryFetch): the same unread-error latch around response.text(), so models.schema and models.list raise the typed error on a cut-off non-2xx body. It is checked after the deadline and caller-abort exits.
  • Review round 4 (830cb26).
    • src/low/errors.ts: a message that is not a string is treated as absent too, and falls back to HTTP <status>. An object with a null toString no longer throws a bare TypeError from errorFromEnvelope.
    • src/sdk/exceptions.ts (toSdkError): retryAfter is forwarded for every code, not only queue_full. main has since shipped the same change (#200), so after merging main this is no longer part of this PR's diff; its extra exceptions.test.ts cases for rate_limited/service_unavailable/not_found stay.
    • src/sdk/models.ts (describeValidationFailures): only string/number loc segments and a string type are stringified, so a hostile detail[] entry still yields the typed error.
  • CHANGELOG.md — Fixed entries under [Unreleased]: the unread body (run, schema, list and the queue calls), the code-to-class lookups in both layers (plus the non-string message), and the non-throwing validation summary. (The retryAfter entry was dropped when merging main, whose own Fixed entry already covers it.)
  • src/sdk/retry.ts is untouched.

Judgment calls

  • let unreadError = null as Response | null; is asserted, not annotated. With : Response | null = null, TypeScript narrows it to null in the outer catch, because it does not see the assignment made in the inner catch. tsc then typed unreadError.status as never in send(). A comment in the code explains this.
  • The runLoop/stamping refactor of models.run (#152) has since landed on main; merging it moved the unread-body latch into runLoop() unchanged. It sits just before the existing out-of-budget stampIdempotencyKey exit, and the typed error it throws already carries idempotencyKey. On the queue path, send() runs inside submit's stamping wrapper, so the unread-body RouterError gets the key there too. The queued-result rewrite of send() is still open and will need the same small rebase.

Tests

models.test.ts, new describe block "comfy.models.run when an error body is cut off mid-read":

  • 422 + invalid_input, body cut, retry: false → ComfyError with httpStatus 422, code invalid_input, requestId and idempotencyKey set, and cause a TypeError.
  • 402 + insufficient_credits, body cut → InsufficientCredits, with cause set.
  • Retries still enabled: one cut, then a full 422 → the error carries the parsed detail, has no cause, and the server saw 2 requests.
  • 200 with the body cut → still the raw TypeError.
  • resetTimes (no response) with retry: false → still the raw TypeError.
  • constructor, toString and __proto__ as X-Comfy-Error-Type with the body cut → a ComfyError carrying that code.

errors.test.ts (low layer): an envelope code of constructor, toString, __proto__ or hasOwnProperty → a plain ApiError carrying that code. A non-string code (an array, an object with a null toString, a number) → a plain ApiError with the status-derived code.

A non-string message (an array, an object with a null toString, a number) → HTTP <status>.

exceptions.test.ts: toSdkError on an ApiError whose code is one of those four names → a plain ComfyError carrying that code. retryAfter: 120 on a rate_limited, service_unavailable or not_found error → carried onto the ComfyError.

models.test.ts also covers a 422 whose detail[] entries have a { toString: null } type and loc segment → the typed invalid_input error with message 2 validation errors: body: invalid; body.seed: too small.

models-discovery.test.ts: models.schema answering 404 + model_not_found with the body cut → NotFound with the request id and a TypeError cause, after one request. A 200 with the body cut → still the raw TypeError.

modelRequests.test.ts:

  • submit answering 422 + invalid_input with the body cut → routerErrors.InvalidInput with httpStatus 422, errorType invalid_input, requestId and cause set, message HTTP 422 and detail [].
  • handle().status() answering a scripted 404 + request_not_found with the body cut → RequestNotFound with requestId and cause, and cause not among Object.keys(err).
  • 201 with the body cut → still the raw TypeError.

I stashed the two source changes and re-ran the tests: the three typed-error tests fail without the fix and pass with it. In the same way, the hasOwn test and the non-enumerable cause test fail when their fixes are reverted.

Residual

  • Undici body-inactivity timeouts. withInactivityLimits sets undici's bodyTimeout from the remaining budget. If undici's body timer fires on a non-2xx before the composed AbortSignal does, the read failure is not a TimeoutError, so isTimeout does not catch it. Once retries run out, it now surfaces as the typed HTTP error with the undici error as cause, where before it was the raw error. The two timers come from the same budget, so this is an edge case, and the ticket's "deadline is never re-dressed" is about the signal deadline, which is still checked first. I did not write a test that forces undici's timer to win.
  • The queued-result read path from the open send() rewrite (classify-before-read plus readBodyWithin on the result route) is not on main, so this PR does not touch it. When that PR rebases, it needs the same inner try/catch around its readBodyWithin read, and isTooLarge must stay ahead of the new check.
  • Verified locally on Node 26.4.0. The first commit's tests passed in CI on Node 22 and 24.
  • The queue path still buffers with a bare response.text() with no size cap. That predates this PR and is proposed as a follow-up.

Provenance

  • Authored by: agent-work loop
  • Verified:
    • Head c7413f2 merges current main (1d6b3cd, #152). Resolved conflicts: models.ts (re-applied the latch inside runLoop()), modelRequests.test.ts (kept both describe blocks) and CHANGELOG.md (kept both Fixed entries).
    • pnpm test at c7413f2: 814 passed, 5 skipped, 0 failed.
    • pnpm lint, pnpm format:check, pnpm typecheck, pnpm check:spec-drift and pnpm build: clean at c7413f2.
    • Every new typed-error, own-property lookup, non-string code/message and validation-summary test failed with its source change reverted (earlier rounds).
    • CI on c7413f2: all checks green, including the five required ones (Test node 22/24, build-check, cla-assistant, public-repo-hygiene) and sdk-parity.
  • Deviations:
    • One extra test beyond the plan: no response at all → the raw error with retry: false.
    • unreadError is declared with a type assertion instead of an annotation (see Judgment calls).
    • cause on the queue path is set through defineProperty rather than added to the public RouterErrorOptions.
    • InvalidInput.detail stays [] on an unread body (documented) rather than becoming nullable.
    • No status-to-bucket fallback in models.run for header-less errors; the duplicated X-Comfy-Error-Type finding was declined (Router sends that header once).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Failed requests now report the typed error associated with the server’s response when its error body cannot be read, preserving response details and the underlying read failure.
    • Requests continue to retry when retries remain. Failures reading successful responses and other existing error cases retain their previous behavior.
    • Unrecognized or non-string error codes now fall back to the appropriate general error type instead of being treated as mapped error types.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Comfy-Org/comfy-typescript-sdk/pull/196 **Author:** [@mattmillerai](https://github.com/mattmillerai) **Created:** 9/29/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `mattmiller/be-17634-typed-error-on-unread-body` --- ### 📝 Commits (8) - [`df14fa2`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/df14fa2b6f5054e3c4bdb409d06575f1f9712229) fix(models): raise the typed error when a non-2xx body read fails and no retry remains - [`268799d`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/268799df66a4e3b45747a2cc4c35764652f1562e) fix(models): own-property bucket lookup, and a non-enumerable queue cause - [`c0f95bd`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/c0f95bd69408356d9552f4522f67885c8f58b304) fix(errors): own-property code lookup in the low layer, and an honest message for a cut-off queue 422 - [`53aec07`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/53aec073ba95cc3ad96cdfdecf2415a8bcb06c35) fix(errors): own-property lookup in toSdkError, a string-only envelope code, and a typed error for a cut-off discovery body - [`830cb26`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/830cb262d0c88c1efcd63b76ae43ec72438f0943) fix(errors): string-only envelope message, retryAfter on every sdk error, and a non-throwing validation summary - [`6ec46e7`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/6ec46e7e5571c0de2ac31f71b8c1eea4ec24dd60) Merge remote-tracking branch 'origin/main' into mattmiller/be-17634-typed-error-on-unread-body - [`f9b5a51`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/f9b5a51ef0ec31c4306c744b70c12b7ebc0ccdb2) Merge remote-tracking branch 'origin/main' into mattmiller/be-17634-typed-error-on-unread-body - [`c7413f2`](https://github.com/Comfy-Org/comfy-typescript-sdk/commit/c7413f2794b1366c968b026cd519d90b74782574) Merge remote-tracking branch 'origin/main' into mattmiller/be-17634-typed-error-on-unread-body ### 📊 Changes **12 files changed** (+444 additions, -14 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+23 -0) 📝 `src/low/errors.test.ts` (+32 -0) 📝 `src/low/errors.ts` (+12 -3) 📝 `src/sdk/exceptions.test.ts` (+20 -0) 📝 `src/sdk/exceptions.ts` (+4 -1) 📝 `src/sdk/modelRequests.test.ts` (+62 -0) 📝 `src/sdk/modelRequests.ts` (+36 -1) 📝 `src/sdk/models-discovery.test.ts` (+28 -0) 📝 `src/sdk/models.test.ts` (+130 -0) 📝 `src/sdk/models.ts` (+55 -5) 📝 `src/sdk/routerErrors.ts` (+7 -2) 📝 `test/support/router-stub-server.ts` (+35 -2) </details> ### 📄 Description ## ELI-5 When Router answers with an error (say a 422 or a 402) and the connection drops while the SDK is still reading the error's body, the SDK used to give up with a bare `TypeError: terminated` — even though the status line and headers had already told it exactly what went wrong. Now, once retries are used up, it throws the proper typed error (`InsufficientCredits`, `InvalidInput`, …) built from that status and those headers, and keeps the network failure as `cause`. ## What changed - **`src/sdk/models.ts` (`models.run`)** — the retry loop remembers a non-2xx response whose `readBodyWithin` call failed (`unreadError`). Only that read is wrapped; `fetch` and the classification are not. In the outer catch the order stays as it was (`isTooLarge` → `isTimeout` → caller abort → `nextDelayMs()`). Only when `nextDelayMs()` returns `null` and `unreadError` is set does it throw `errorFromResponse(unreadError, "", idempotencyKey, exc)`. Because that check comes after the three early exits, a cap breach, a deadline or a caller abort is never re-dressed. `errorFromResponse` gets an optional 4th `cause` parameter, passed into the options only when it is provided, so existing call sites are unchanged. It now looks up the bucket class with `Object.hasOwn`, like `toRouterError` already did, because on this path the header is the only input and a `constructor`/`toString` header must not resolve off `Object.prototype`. - **`src/sdk/modelRequests.ts` (`send()`)** — the same pattern around `response.text()`: `toRouterError(status, headers, null)`, with `cause` set through `Object.defineProperty` as a non-enumerable property. That is the shape ES2022's `new Error(msg, { cause })` gives the `models.run` twin. `toRouterError`'s signature and the public `RouterErrorOptions` are unchanged. - **`test/support/router-stub-server.ts`** — a new `cutBodyTimes` knob. It sends the ordinary status and headers with no `Content-Length`, writes `{"detail":[`, then destroys the socket. This sits between `resetTimes` (no status line at all) and `stallBody` (never drops). `ScriptedResponse` also gets a `cutBody` flag, so one scripted route can drop its body, and the `respond` doc now lists `cutBodyTimes` among the checks that run before it. - **`src/low/errors.ts` (`errorFromEnvelope`)** — review round 2. The Comfy API v2 code-to-class lookup now uses `Object.hasOwn(BY_CODE, code)` too. Before this, an envelope `code` of `constructor` or `toString` resolved off `Object.prototype`. - **Queue path message** — review round 2. On the cut-off path, `send()` passes `toRouterError` a string `detail` of `HTTP <status>`. A 422 whose body was never read now reads `HTTP 422`, like the `models.run` twin, rather than `InvalidInput`'s summary of an empty `detail[]`. The `InvalidInput.detail` doc in `src/sdk/routerErrors.ts` now covers the unread-body case, where `cause` is set. - **Review round 3 (53aec07).** - `src/sdk/exceptions.ts` (`toSdkError`): the `sdk`-layer code-to-class lookup now uses `Object.hasOwn(BY_CODE, exc.code)`. Before, the `ApiError` that `errorFromEnvelope` built safely was looked up by prototype chain again one hop later. - `src/low/errors.ts`: a `code` that is not a string is treated as absent, and the status-derived code is used instead. - `src/sdk/models.ts` (`discoveryFetch`): the same unread-error latch around `response.text()`, so `models.schema` and `models.list` raise the typed error on a cut-off non-2xx body. It is checked after the deadline and caller-abort exits. - **Review round 4 (830cb26).** - `src/low/errors.ts`: a `message` that is not a string is treated as absent too, and falls back to `HTTP <status>`. An object with a null `toString` no longer throws a bare `TypeError` from `errorFromEnvelope`. - `src/sdk/exceptions.ts` (`toSdkError`): `retryAfter` is forwarded for every code, not only `queue_full`. `main` has since shipped the same change (#200), so after merging `main` this is no longer part of this PR's diff; its extra `exceptions.test.ts` cases for `rate_limited`/`service_unavailable`/`not_found` stay. - `src/sdk/models.ts` (`describeValidationFailures`): only string/number `loc` segments and a string `type` are stringified, so a hostile `detail[]` entry still yields the typed error. - **`CHANGELOG.md`** — `Fixed` entries under `[Unreleased]`: the unread body (`run`, `schema`, `list` and the queue calls), the code-to-class lookups in both layers (plus the non-string `message`), and the non-throwing validation summary. (The `retryAfter` entry was dropped when merging `main`, whose own `Fixed` entry already covers it.) - `src/sdk/retry.ts` is untouched. ### Judgment calls - `let unreadError = null as Response | null;` is asserted, not annotated. With `: Response | null = null`, TypeScript narrows it to `null` in the outer catch, because it does not see the assignment made in the inner catch. `tsc` then typed `unreadError.status` as `never` in `send()`. A comment in the code explains this. - The `runLoop`/`stamping` refactor of `models.run` (#152) has since landed on `main`; merging it moved the unread-body latch into `runLoop()` unchanged. It sits just before the existing out-of-budget `stampIdempotencyKey` exit, and the typed error it throws already carries `idempotencyKey`. On the queue path, `send()` runs inside `submit`'s `stamping` wrapper, so the unread-body `RouterError` gets the key there too. The queued-result rewrite of `send()` is still open and will need the same small rebase. ## Tests `models.test.ts`, new describe block "comfy.models.run when an error body is cut off mid-read": - 422 + `invalid_input`, body cut, `retry: false` → `ComfyError` with `httpStatus` 422, `code` `invalid_input`, `requestId` and `idempotencyKey` set, and `cause` a `TypeError`. - 402 + `insufficient_credits`, body cut → `InsufficientCredits`, with `cause` set. - Retries still enabled: one cut, then a full 422 → the error carries the parsed `detail`, has no `cause`, and the server saw 2 requests. - 200 with the body cut → still the raw `TypeError`. - `resetTimes` (no response) with `retry: false` → still the raw `TypeError`. - `constructor`, `toString` and `__proto__` as `X-Comfy-Error-Type` with the body cut → a `ComfyError` carrying that code. `errors.test.ts` (low layer): an envelope `code` of `constructor`, `toString`, `__proto__` or `hasOwnProperty` → a plain `ApiError` carrying that code. A non-string `code` (an array, an object with a null `toString`, a number) → a plain `ApiError` with the status-derived code. A non-string `message` (an array, an object with a null `toString`, a number) → `HTTP <status>`. `exceptions.test.ts`: `toSdkError` on an `ApiError` whose code is one of those four names → a plain `ComfyError` carrying that code. `retryAfter: 120` on a `rate_limited`, `service_unavailable` or `not_found` error → carried onto the `ComfyError`. `models.test.ts` also covers a 422 whose `detail[]` entries have a `{ toString: null }` `type` and `loc` segment → the typed `invalid_input` error with message `2 validation errors: body: invalid; body.seed: too small`. `models-discovery.test.ts`: `models.schema` answering 404 + `model_not_found` with the body cut → `NotFound` with the request id and a `TypeError` cause, after one request. A 200 with the body cut → still the raw `TypeError`. `modelRequests.test.ts`: - `submit` answering 422 + `invalid_input` with the body cut → `routerErrors.InvalidInput` with `httpStatus` 422, `errorType` `invalid_input`, `requestId` and `cause` set, message `HTTP 422` and `detail` `[]`. - `handle().status()` answering a scripted 404 + `request_not_found` with the body cut → `RequestNotFound` with `requestId` and `cause`, and `cause` not among `Object.keys(err)`. - 201 with the body cut → still the raw `TypeError`. I stashed the two source changes and re-ran the tests: the three typed-error tests fail without the fix and pass with it. In the same way, the `hasOwn` test and the non-enumerable `cause` test fail when their fixes are reverted. ## Residual - **Undici body-inactivity timeouts.** `withInactivityLimits` sets undici's `bodyTimeout` from the remaining budget. If undici's body timer fires on a non-2xx before the composed `AbortSignal` does, the read failure is not a `TimeoutError`, so `isTimeout` does not catch it. Once retries run out, it now surfaces as the typed HTTP error with the undici error as `cause`, where before it was the raw error. The two timers come from the same budget, so this is an edge case, and the ticket's "deadline is never re-dressed" is about the signal deadline, which is still checked first. I did not write a test that forces undici's timer to win. - **The queued-result read path from the open `send()` rewrite** (classify-before-read plus `readBodyWithin` on the result route) is not on `main`, so this PR does not touch it. When that PR rebases, it needs the same inner `try/catch` around its `readBodyWithin` read, and `isTooLarge` must stay ahead of the new check. - Verified locally on Node 26.4.0. The first commit's tests passed in CI on Node 22 and 24. - The queue path still buffers with a bare `response.text()` with no size cap. That predates this PR and is proposed as a follow-up. ## Provenance - **Authored by:** agent-work loop - **Verified:** - Head c7413f2 merges current `main` (1d6b3cd, #152). Resolved conflicts: `models.ts` (re-applied the latch inside `runLoop()`), `modelRequests.test.ts` (kept both describe blocks) and `CHANGELOG.md` (kept both `Fixed` entries). - `pnpm test` at c7413f2: 814 passed, 5 skipped, 0 failed. - `pnpm lint`, `pnpm format:check`, `pnpm typecheck`, `pnpm check:spec-drift` and `pnpm build`: clean at c7413f2. - Every new typed-error, own-property lookup, non-string code/message and validation-summary test failed with its source change reverted (earlier rounds). - CI on c7413f2: all checks green, including the five required ones (Test node 22/24, build-check, cla-assistant, public-repo-hygiene) and sdk-parity. - **Deviations:** - One extra test beyond the plan: no response at all → the raw error with `retry: false`. - `unreadError` is declared with a type assertion instead of an annotation (see Judgment calls). - `cause` on the queue path is set through `defineProperty` rather than added to the public `RouterErrorOptions`. - `InvalidInput.detail` stays `[]` on an unread body (documented) rather than becoming nullable. - No status-to-bucket fallback in `models.run` for header-less errors; the duplicated `X-Comfy-Error-Type` finding was declined (Router sends that header once). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Failed requests now report the typed error associated with the server’s response when its error body cannot be read, preserving response details and the underlying read failure. * Requests continue to retry when retries remain. Failures reading successful responses and other existing error cases retain their previous behavior. * Unrecognized or non-string error codes now fall back to the appropriate general error type instead of being treated as mapped error types. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/comfy-typescript-sdk#198
No description provided.