Repository navigation
fix(core): resolve cross-runtime fetch and error-handling issues in gaxios, teeny-request, and gcp-metadata - #9519
Draft
danieljbruce wants to merge 1 commit into
Draft
danieljbruce wants to merge 1 commit into
danieljbruce wants to merge 1 commit into
Conversation
Contributor
|
Warning Gemini encountered an error creating the review. You can try again by commenting |
danieljbruce
marked this pull request as draft
October 5, 2026 20:44
danieljbruce
force-pushed
the
fix/bun-client-library-issues
branch
from
October 6, 2026 17:55
702f2e9 to
e8fd33e
Compare
… teeny-request, and gcp-metadata
- teeny-request: delegate default `fetch` on Bun to `globalThis.fetch` (or `globalThis.__googleCloudBunFetch` when shimmed) with `Readable.toWeb` request stream conversion so `import('node-fetch')` is not hijacked by `bun:node-fetch` (which bypasses `globalThis.fetch` and `nock` v14), pass `signal: AbortSignal.timeout(reqOpts.timeout)` in `requestToFetchOptions`, populate `proxy` and `tls` options alongside `agent` while respecting `NO_PROXY`, and convert Web `ReadableStream` responses to Node `stream.Readable` via `Readable.fromWeb` in stream mode without prematurely locking the stream in callback mode.
- gaxios: delegate `Gaxios.#getFetch()` on Bun to `globalThis.fetch` (or `globalThis.__googleCloudBunFetch` when shimmed) with `Readable.toWeb` request stream conversion and lazy `Readable.fromWeb` response stream wrapping, forward `proxy` and `tls` options to `fetchImplementation` in `_defaultAdapter` (and remove `proxy` when `noProxy` matches), preserve non-`Error` `AbortController.abort(reason)` values with `code = 'AbortError'` instead of falling back to `'Unexpected Gaxios Error'`, and normalize `TimeoutError` signal reasons and messages across `node-fetch` and native `fetch` (`V8` and `JavaScriptCore`).
- gcp-metadata: pass `fetchImplementation` on Bun so metadata requests route through `globalThis.fetch`, unwrap nested error codes (`AggregateError.errors`, `.cause`, `.error`) in `isAvailable`, and ensure `AggregateError` from `Promise.any` in `fastFailMetadataRequest` and `isAvailable` (`MetadataLookupWarning`) always has a non-empty error message on runtimes (such as `JavaScriptCore`/Bun) where `Promise.any` initializes `AggregateError.message` to `""`.
danieljbruce
force-pushed
the
fix/bun-client-library-issues
branch
from
October 6, 2026 18:02
e8fd33e to
f2f15d1
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes production client library issues across
gaxios,teeny-request, andgcp-metadataexposed when running on non-V8 / standardfetchruntimes (such as Bun):teeny-requestrequest timeouts (src/index.ts):requestToFetchOptionspreviously only passed{timeout: reqOpts.timeout}, a non-standardnode-fetchextension ignored by standardfetch/bun:node-fetch.signal: AbortSignal.timeout(reqOpts.timeout)whenreqOpts.timeoutis configured so timeouts are enforced across runtimes.gaxiosandteeny-requestproxy & mTLS options (gaxios/src/gaxios.ts,teeny-request/src/agents.ts,teeny-request/src/index.ts):fetchimplementation ignoresinit.agent(such as Bun) previously ignoredHTTPS_PROXY/HTTP_PROXY/proxyand mTLScert/keyconfiguration, or failed to bypassopts.proxywhennoProxymatched.gaxios(_defaultAdapter) andteeny-request(requestToFetchOptions) now populateproxyandtlsoptions on thefetchinit object alongsideagent, and ensureproxyis omitted whennoProxy/NO_PROXYexcludes the target URL.gaxiosnon-Errorabort reasons and timeout retries (gaxios/src/gaxios.ts,gaxios/src/retry.ts):AbortController.abort(reason)is called with a non-Errorreason (e.g. a string), standardfetchrejects withsignal.reasonverbatim.Gaxios._requestpreviously fell intonew GaxiosError('Unexpected Gaxios Error', ...)withoutcode = 'AbortError'and discarded the reason string. It now preserves the reason in the error message and setserr.code = 'AbortError'.AbortSignal.timeouttriggers,node-fetchthrows a genericAbortError(code = undefined) even thoughopts.signal.reasonisDOMException { name: 'TimeoutError' }, causing timeout retry behavior to diverge betweennode-fetchand native/Bunfetch.Gaxios._requestandshouldRetryRequestnow inspectsignal.reasonso timeout retries behave consistently across runtimes.teeny-requestWebReadableStreamresponse body handling (teeny-request/src/index.ts):teenyRequestnow converts WebReadableStreambodies (!(res.body instanceof Readable)) to Nodestream.ReadableviaReadable.fromWebbefore attaching stream listeners, while avoiding lockingres.bodyprior tores.json()/res.text()in callback mode and safely handlingnullresponse bodies (such as204 No Content).gcp-metadataemptyAggregateError.message(gcp-metadata/src/index.ts):fastFailMetadataRequest(Promise.any), JavaScriptCore (Bun) constructs anAggregateErrorwith an empty.message(""), causingMetadataLookupWarninginisAvailable()to emitreceived unexpected error = code = ....fastFailMetadataRequestnow normalizes an emptyAggregateError.message, andisAvailable()falls back to extracting child error messages whene.messageis empty.Testing
core/packages/gcp-metadata/test/index.test.ts,core/packages/teeny-request/test/index.ts,core/packages/gaxios/test/test.getch.ts, andcore/packages/gaxios/test/test.retry.ts.