{"record":{"id":"9144f086e37caacd","repo":"ruvnet/ruflo","slug":"fetchagentcard-sourceurl-did-not-return-valid","errorCode":null,"errorMessage":"fetchAgentCard: ${sourceUrl} did not return valid JSON","messagePattern":"fetchAgentCard: (.+?) did not return valid JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugin-agent-federation/src/a2a/consume.ts","lineNumber":77,"sourceCode":"      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) {\n    throw new Error(\n      `fetchAgentCard: invalid A2A agent card from ${sourceUrl}: ${validation.errors.join('; ')}`,\n    );\n  }\n  return { card: parsed as A2AAgentCard, sourceUrl };\n}\n\n/**\n * Fetch a remote Agent Card and register the peer in federation discovery.\n * The peer enters at TrustLevel.UNTRUSTED (see fromAgentCard) — the card is\n * self-asserted metadata, and trust is earned through the normal handshake.\n */\nexport async function consumeAgentCard(\n  discovery: DiscoveryService,","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugin-agent-federation/src/a2a/consume.ts#L59-L95","documentation":"The Agent Card endpoint returned 2xx but JSON.parse of the body threw, so the payload is not valid JSON. Typical bodies are HTML (login pages, error templates), plain-text errors, empty responses, or JSON with syntax problems. The abort-timer cleanup has already run by the time this is thrown.","triggerScenarios":"Auth walls or captive portals returning HTML with status 200; the server returning JSONP or commented JSON; an empty 200 response; corporate proxies rewriting the body despite the accept header.","commonSituations":"Intercepting proxies that inject terms-of-service pages; misconfigured content negotiation on the peer server; endpoints that serve human-readable status text on the card route.","solutions":["curl the exact sourceUrl and inspect the raw body — the first 200 bytes usually reveal the problem","Fix the serving side to emit application/json on the well-known card route","If a proxy rewrites responses, bypass it or configure an exception for the card URL","Check for a trailing BOM or encoding mismatch if the body looks like JSON but still fails"],"exampleFix":"// before\nawait fetchAgentCard(url);\n// after — inspect what the endpoint really returns\nconst res = await fetch(resolveAgentCardUrl(url));\nconsole.log(res.headers.get('content-type'));\nconsole.log((await res.text()).slice(0, 200));","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const { card } = await fetchAgentCard(url, opts);\n} catch (e) {\n  if (String(e).includes('did not return valid JSON')) {\n    // fetch the body yourself once to see what the endpoint really returns\n    const res = await fetchImpl(resolveAgentCardUrl(url));\n    console.error('non-JSON body:', (await res.text()).slice(0, 200));\n  }\n  throw e;\n}","preventionTips":["curl the card endpoint when adding a peer","Watch for intercepting proxies and captive portals in corporate networks","Serve application/json explicitly on the well-known route","Add a smoke test that fetches and parses your own card in CI"],"tags":["json","network","a2a"],"backgroundTag":"invalid-json-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"}