{"record":{"id":"b4899824f483cb46","repo":"Egonex-AI/Understand-Anything","slug":"source-unavailable","errorCode":null,"errorMessage":"Source unavailable","messagePattern":"Source unavailable","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx","lineNumber":171,"sourceCode":"    }\n\n    if (accessToken === \"__demo__\") {\n      setState({\n        status: \"error\",\n        source: null,\n        error: \"Source preview is available only when the local dashboard server is running.\",\n      });\n      return;\n    }\n\n    const controller = new AbortController();\n    setState({ status: \"loading\", source: null, error: null });\n\n    fetch(fileContentUrl(node.filePath, accessToken), { signal: controller.signal })\n      .then(async (res) => {\n        const data = (await res.json()) as SourceFile | { error?: string };\n        if (!res.ok) {\n          throw new Error(\"error\" in data && data.error ? data.error : \"Source unavailable\");\n        }\n        setState({ status: \"loaded\", source: data as SourceFile, error: null });\n      })\n      .catch((err: unknown) => {\n        if (controller.signal.aborted) return;\n        setState({\n          status: \"error\",\n          source: null,\n          error: err instanceof Error ? err.message : String(err),\n        });\n      });\n\n    return () => controller.abort();\n  }, [accessToken, node?.filePath]);\n\n  const highlightedRange = useMemo(() => {\n    if (!node?.lineRange) return null;\n    return { start: node.lineRange[0], end: node.lineRange[1] };","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/Egonex-AI/Understand-Anything/blob/32944829e7a63a9fa9c55d811d7f98a9530c6a6a/understand-anything-plugin/packages/dashboard/src/components/CodeViewer.tsx#L153-L189","documentation":"Thrown by CodeViewer when fetch(fileContentUrl(node.filePath, accessToken)) returns a non-ok response whose JSON body has no usable error field. It is the fallback message for 'the server refused the file-content request but did not tell us why'. Any body.error present is preferred; only when absent does this string surface, and it lands in the viewer's error state (and via setState into the UI).","triggerScenarios":"The /file-content.json endpoint returns non-200 (e.g. 403 token mismatch, 404 file not in the graph-derived allowlist, 500 read failure) AND the response body is either not JSON, or is JSON without an error key. The thrown Error('Source unavailable') propagates to the .catch which sets status:'error' and error:'Source unavailable'.","commonSituations":"Access token mismatch between dashboard client and server. The filePath is outside the graph-derived allowlist the server enforces (so it 404s with an empty/body-less response). Demo mode is not active but the local server is not actually running (fileContentUrl points at an unreachable host). The file was deleted from disk after the graph was built. Symbolic-link / permission issue server-side.","solutions":["Confirm the local dashboard dev server is running and reachable (the demo-mode guard at line 155 already steers demo users away; this error means the live fetch itself failed).","Verify the access token matches UA_DASHBOARD_ACCESS_TOKEN on the server.","Open the fileContentUrl directly in the browser to see the raw response body and status — the server often returns a JSON error that the catch swallows.","Confirm node.filePath is within the server's allowlist (paths derived from the loaded graph) — files outside it are refused.","If the file was removed from disk, regenerate the graph so it no longer references the missing path."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before opening the viewer, confirm a live server and an allowlisted path\nfunction canFetchSource(accessToken, filePath, graph) {\n  if (accessToken === '__demo__') return false; // demo mode has no server\n  if (!filePath) return false;\n  return graph?.nodes.some((n) => n.filePath === filePath) ?? false; // allowlist\n}","typeGuard":null,"tryCatchPattern":"fetch(fileContentUrl(node.filePath, accessToken), { signal: controller.signal })\n  .then(async (res) => {\n    const data = await res.json().catch(() => ({}));\n    if (!res.ok) {\n      throw new Error((data && data.error) ? data.error : 'Source unavailable');\n    }\n    return data;\n  })\n  .catch((err) => {\n    if (controller.signal.aborted) return;\n    setState({ status: 'error', source: null, error: err.message });\n  });","preventionTips":["Ensure the local dashboard server is running when not in demo mode.","Keep the access token identical on client and server.","Regenerate the graph if the file was removed from disk so the allowlist no longer references it.","Open fileContentUrl directly to read the server's error body when the message is generic."],"tags":["dashboard","network","fetch","code-viewer","file-content"],"backgroundTag":null,"analyzedSha":"32944829e7a63a9fa9c55d811d7f98a9530c6a6a","analyzedAt":"2026-08-12T10:25:44.261Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}