Skip to content

@ai-sdk/provider-utils: module-scope Function.prototype.toString.call(globalThis.fetch) throws in environments without fetch #18528

Description

@brianstrauch

Description

@ai-sdk/provider-utils@5.0.15 and later cannot be imported at all in any environment where globalThis.fetch is undefined. Because ai depends on it, ai becomes unimportable in those environments too.

safe-node-fetch evaluates this at module scope:

var initialGlobalFetch = globalThis.fetch;
var initialGlobalFetchIsNodeDefault = isNodeDefaultFetch(initialGlobalFetch);

function isNodeDefaultFetch(fetch) {
  const source = Function.prototype.toString.call(fetch);
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
}

Function.prototype.toString.call(undefined) throws:

TypeError: Function.prototype.toString requires that 'this' be a Function

Since the call runs at import time rather than lazily, there is no way for a consumer to avoid it — importing the package is enough to crash.

Expected: importing the package succeeds regardless of whether a global fetch exists; the safe-node-fetch optimization is skipped when it does not apply.

Actual: import throws TypeError.

Bisected: introduced in @ai-sdk/provider-utils@5.0.15. 5.0.14 and earlier are fine. Present through 5.0.23 (current latest). First reaches ai users via ai@7.0.42.

Real-world impact

Temporal's TypeScript SDK runs workflow code in a deterministic V8 sandbox that intentionally omits fetch (network I/O is not allowed in workflow code — it happens in activities). As of ai@7.0.42, importing ai inside a workflow throws at import time, so every workflow task fails:

TypeError: Function.prototype.toString requires that 'this' be a Function
    at isNodeDefaultFetch (safe-node-fetch.ts:135:45)
    at ./node_modules/@ai-sdk/provider-utils/dist/index.js (safe-node-fetch.ts:106:40)
    at importWorkflows (workflows-autogenerated-entrypoint.cjs:13:9)

This should also affect other fetch-less contexts, such as Node running with --no-experimental-fetch and restricted VM/isolate embeddings.

Suggested fix

Guard the type before calling toString:

function isNodeDefaultFetch(fetch) {
  if (typeof fetch !== 'function') return false;
  const source = Function.prototype.toString.call(fetch);
  return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
}

Ideally also defer the check so it is only evaluated when a download fetch is actually needed, rather than as a side effect of importing the module.

Reproduction

Minimal, no framework required:

npm i @ai-sdk/provider-utils@5.0.23
node -e "delete globalThis.fetch; require('@ai-sdk/provider-utils')"
# TypeError: Function.prototype.toString requires that 'this' be a Function

Passes on 5.0.14, throws on 5.0.15 and later. The control case (fetch present) imports fine.

AI SDK Version

  • @ai-sdk/provider-utils: 5.0.15 through 5.0.23 (5.0.14 unaffected)
  • ai: 7.0.42 and later (7.0.41 unaffected)

Code of Conduct

I agree to follow this project's Code of Conduct.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions