{"record":{"id":"430673c20c77ce8d","repo":"ruvnet/ruflo","slug":"fetchagentcard-sourceurl-returned-http-res-s","errorCode":null,"errorMessage":"fetchAgentCard: ${sourceUrl} returned HTTP ${res.status}","messagePattern":"fetchAgentCard: (.+?) returned HTTP (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugin-agent-federation/src/a2a/consume.ts","lineNumber":63,"sourceCode":"  options: FetchAgentCardOptions = {},\n): Promise<FetchAgentCardResult> {\n  const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n  if (typeof fetchImpl !== 'function') {\n    throw new Error('fetchAgentCard: no fetch implementation available');\n  }\n  const sourceUrl = resolveAgentCardUrl(baseOrCardUrl);\n  const maxBytes = options.maxBytes ?? 256 * 1024;\n\n  const controller = new AbortController();\n  const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 10_000);\n  let text: string;\n  try {\n    const res = await fetchImpl(sourceUrl, {\n      signal: controller.signal,\n      headers: { accept: 'application/json' },\n    });\n    if (!res.ok) {\n      throw new Error(`fetchAgentCard: ${sourceUrl} returned HTTP ${res.status}`);\n    }\n    text = await res.text();\n  } finally {\n    clearTimeout(timer);\n  }\n  if (text.length > maxBytes) {\n    throw new Error(`fetchAgentCard: card exceeds ${maxBytes} bytes`);\n  }\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(text);\n  } catch {\n    throw new Error(`fetchAgentCard: ${sourceUrl} did not return valid JSON`);\n  }\n\n  const validation = validateAgentCard(parsed);\n  if (!validation.valid) {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugin-agent-federation/src/a2a/consume.ts#L45-L81","documentation":"The Agent Card HTTP request completed at the transport layer but the response status was outside 2xx (res.ok false). The body is never read; instead the resolved source URL and the HTTP status are reported. Note the request already carries an accept: application/json header and a default 10s abort timeout, so this error is purely about the server's status code.","triggerScenarios":"Wrong base URL where the well-known agent.json path is not served (404); the peer or an intermediary requires authentication (401/403); reverse proxy misroute or upstream down (502/503); the card endpoint moved without a redirect.","commonSituations":"Typo'd or stale peer hostname; peer deployed behind a gateway needing a path prefix; transient 5xx while the peer redeploys; firewall returning 403 for unknown egress IPs.","solutions":["Open the exact sourceUrl from the error message with curl or a browser and confirm the card is served","Fix scheme/host/path — remember the resolver appends the well-known path when you pass a base URL","For 401/403 supply the auth the peer's edge requires or get your egress IP whitelisted","Retry transient 5xx with backoff; the call is a plain fetch you control, so wrap it in your own retry loop"],"exampleFix":"// before\nconst { card } = await fetchAgentCard(peerUrl);\n// after\nlet card;\nfor (let i = 0; i < 3; i++) {\n  try { card = (await fetchAgentCard(peerUrl)).card; break; }\n  catch (e) {\n    if (!String(e).includes('HTTP 5') || i === 2) throw e;\n    await new Promise(r => setTimeout(r, 2 ** i * 250));\n  }\n}","handlingStrategy":"retry","validationCode":"// pre-flight the URL cheaply before the card fetch\ntry {\n  const u = new URL(base);\n  await fetch(u.origin, { method: 'HEAD' });\n} catch {\n  throw new Error('peer origin unreachable: ' + base);\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  try {\n    return await fetchAgentCard(url, opts);\n  } catch (e) {\n    const msg = String(e);\n    const transient = msg.includes('HTTP 5') || msg.includes('HTTP 429');\n    if (!transient || attempt === 2) throw e;\n    await new Promise(r => setTimeout(r, 2 ** attempt * 250));\n  }\n}","preventionTips":["Verify the well-known card URL with curl when onboarding a peer","Treat 4xx (other than 429) as permanent config errors, not retries","Monitor peer availability and cache validated cards with a TTL","Log the resolved sourceUrl on every failure for quick diagnosis"],"tags":["http","network","a2a"],"backgroundTag":"http-error-response","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}