{"record":{"id":"34f0b30d10e264fa","repo":"datawhalechina/hello-agents","slug":"no-reachable-backend-api-tried-candidatebases","errorCode":null,"errorMessage":"No reachable backend API. Tried: ${candidateBases.join(', ')}","messagePattern":"No reachable backend API\\. Tried: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"Co-creation-projects/monkeyhlj-NetworkHealthReportAgent/frontend/src/api.js","lineNumber":40,"sourceCode":"\nconst api = axios.create({ timeout: API_TIMEOUT })\n\nasync function resolveBase() {\n  if (activeBase) {\n    return activeBase\n  }\n\n  for (const base of candidateBases) {\n    try {\n      await axios.get(`${base}/health`, { timeout: 3000 })\n      activeBase = base\n      return base\n    } catch (_) {\n      // Try next candidate\n    }\n  }\n\n  throw new Error(`No reachable backend API. Tried: ${candidateBases.join(', ')}`)\n}\n\nasync function apiGet(path, config = {}) {\n  const base = await resolveBase()\n  return api.get(`${base}${path}`, config)\n}\n\nasync function apiPost(path, body = {}, config = {}) {\n  const base = await resolveBase()\n  return api.post(`${base}${path}`, body, config)\n}\n\nexport async function fetchSites() {\n  const { data } = await apiGet('/sites')\n  return data.sites\n}\n\nexport async function fetchReport(siteId, startDate, endDate) {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/monkeyhlj-NetworkHealthReportAgent/frontend/src/api.js#L22-L58","documentation":"Backend discovery failure: at startup the client builds candidateBases (VITE_API_BASE_URL env var, then http://127.0.0.1:8000/api, http://localhost:8000/api, and ${protocol}//${currentHost}:8000/api) and probes each with GET {base}/health (3s timeout) until one answers. If none do, it throws listing all tried bases. The result is cached in activeBase, so one failure is retried on every call.","triggerScenarios":"Backend not running on port 8000; backend running but /health route missing or erroring; VITE_API_BASE_URL set to a wrong/stale URL; accessing from another device where 127.0.0.1/localhost point nowhere and currentHost:8000 is firewalled; https page probing http:// candidates gets blocked by mixed-content rules.","commonSituations":"Deployed frontend (https) cannot probe http://127.0.0.1:8000 (mixed content) and backend host:8000 is not exposed publicly; VITE_API_BASE_URL typo'd during build; backend started without the /health endpoint; corporate network blocks non-standard ports.","solutions":["Verify the backend serves GET /health: curl -i http://127.0.0.1:8000/api/health (note the /api prefix is part of base)","Set VITE_API_BASE_URL correctly at build time to the publicly reachable, https API origin","Ensure /health exists, returns 200 quickly, and is exempt from auth","For https deployments, serve the API over https or proxy it under the same origin so probes aren't blocked as mixed content"],"exampleFix":"// before\nbases.push('http://127.0.0.1:8000/api');\nbases.push('http://localhost:8000/api');\nbases.push(`${protocol}//${currentHost}:8000/api`);\n\n// after (prefer same-origin proxy in prod; skip http candidates on https pages)\nif (protocol !== 'https:') {\n    bases.push('http://127.0.0.1:8000/api', 'http://localhost:8000/api', `${protocol}//${currentHost}:8000/api`);\n} else {\n    bases.push(`${protocol}//${currentHost}/api`); // reverse-proxied same origin\n}","handlingStrategy":"fallback","validationCode":"async function probe(base) { try { const r = await axios.get(`${base}/health`, { timeout: 3000 }); return r.status === 200; } catch { return false; } } const ok = await probe(VITE_API_BASE_URL ?? defaultBase); if (!ok) showSetupError();","typeGuard":null,"tryCatchPattern":"try { await apiGet('/report'); } catch (e) { if (e.message.startsWith('No reachable backend')) showBackendDownBanner(); else throw e; }","preventionTips":["Set VITE_API_BASE_URL correctly at build time for deployed environments","Serve /health unauthenticated, fast, and with CORS enabled","Avoid http:// candidates on https pages (mixed content)","Clear/re-probe activeBase on failure instead of caching forever"],"tags":["backend-discovery","health-check","deployment","mixed-content"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}