ruvnet/ruflo · error

fetchAgentCard: no fetch implementation available

Error message

fetchAgentCard: no fetch implementation available

What it means

fetchAgentCard() needs an HTTP client: options.fetchImpl if provided, otherwise the global globalThis.fetch. When neither is a function it refuses to start the request. This targets runtimes without a built-in WHATWG fetch (Node.js below 18, older jsdom test setups, some bundler/edge configurations).

Source

Thrown at v3/@claude-flow/plugin-agent-federation/src/a2a/consume.ts:49

 * URL) gets the A2A well-known path appended; anything already pointing at a
 * JSON document is used as-is.
 */
export function resolveAgentCardUrl(baseOrCardUrl: string): string {
  const url = new URL(baseOrCardUrl);
  if (url.pathname === '/' || url.pathname === '') {
    url.pathname = A2A_WELL_KNOWN_PATH;
  }
  return url.toString();
}

/** Fetch and structurally validate a remote A2A Agent Card. */
export async function fetchAgentCard(
  baseOrCardUrl: string,
  options: FetchAgentCardOptions = {},
): Promise<FetchAgentCardResult> {
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
  if (typeof fetchImpl !== 'function') {
    throw new Error('fetchAgentCard: no fetch implementation available');
  }
  const sourceUrl = resolveAgentCardUrl(baseOrCardUrl);
  const maxBytes = options.maxBytes ?? 256 * 1024;

  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 10_000);
  let text: string;
  try {
    const res = await fetchImpl(sourceUrl, {
      signal: controller.signal,
      headers: { accept: 'application/json' },
    });
    if (!res.ok) {
      throw new Error(`fetchAgentCard: ${sourceUrl} returned HTTP ${res.status}`);
    }
    text = await res.text();
  } finally {
    clearTimeout(timer);

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Pass an implementation explicitly: fetchAgentCard(url, { fetchImpl }) using undici's or node-fetch's fetch
  2. Upgrade to Node 18+ or a runtime that ships WHATWG fetch
  3. In tests, inject a stub fetchImpl instead of relying on the global
  4. If you import a fetch polyfill, make sure it runs before this module is used

Example fix

// before
await fetchAgentCard('https://peer.example');
// after
import { fetch as undiciFetch } from 'undici';
await fetchAgentCard('https://peer.example', { fetchImpl: undiciFetch });
Defensive patterns

Strategy: validation

Validate before calling

const fetchImpl =
  typeof globalThis.fetch === 'function'
    ? globalThis.fetch.bind(globalThis)
    : (await import('undici')).fetch;
await fetchAgentCard(url, { fetchImpl });

Type guard

function hasGlobalFetch(): boolean {
  return typeof globalThis.fetch === 'function';
}

Prevention

When it happens

Trigger: Running on Node 16 or earlier where globalThis.fetch is undefined; test environments (older jsdom) that do not install fetch; bundlers that tree-shake or alias the global away; passing options.fetchImpl that is not a function (e.g. a module object).

Common situations: CI images pinned to old Node LTS versions; Electron renderer or sandboxed contexts without fetch; code reused across Node and restricted runtimes.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/ef61248f88e9963e. Report an issue: GitHub.