{"record":{"id":"f2c5043f25c78f02","repo":"AykutSarac/jsoncrack.com","slug":"unable-to-load-graph-renderer","errorCode":null,"errorMessage":"Unable to load graph renderer.","messagePattern":"Unable to load graph renderer\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/chrome-extension/src/content-script.tsx","lineNumber":320,"sourceCode":"// the graph. That's acceptable for the current target (browser JSON viewers\n// on static responses) — revisit if we add SPA support.\nfunction GraphView({ rawJson }: { rawJson: string }) {\n  const theme = useSystemTheme();\n  const [JSONCrackComponent, setJSONCrackComponent] = useState<JSONCrackComponentType | null>(null);\n  const [componentError, setComponentError] = useState<string | null>(null);\n  const [selectedNode, setSelectedNode] = useState<NodeData | null>(null);\n\n  useEffect(() => {\n    let active = true;\n\n    loadJsonCrackComponent()\n      .then(component => {\n        if (!active) return;\n        setJSONCrackComponent(() => component);\n      })\n      .catch((error: unknown) => {\n        if (!active) return;\n        const message = error instanceof Error ? error.message : \"Unable to load graph renderer.\";\n        setComponentError(message);\n      });\n\n    return () => {\n      active = false;\n    };\n  }, []);\n\n  const parsedJson = useMemo(() => {\n    try {\n      return JSON.parse(rawJson);\n    } catch {\n      return null;\n    }\n  }, [rawJson]);\n\n  if (parsedJson === null) {\n    return <div id=\"jsoncrack-graph-error\">JSON parsing failed for graph mode.</div>;","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/AykutSarac/jsoncrack.com/blob/3c9af69e23c635356293b6b28cf4cd0af10d1059/apps/chrome-extension/src/content-script.tsx#L302-L338","documentation":"In the Chrome extension content script, loadJsonCrackComponent() dynamically imports jsoncrack-react (temporarily shadowing the host page's Worker to let ELK fall back to sync layout). If the import rejects and the thrown value is not an Error instance, this fallback string is stored as componentError. Failures usually stem from the host page's Content Security Policy blocking the module/chunk or blob: worker creation, a missing web_accessible_resources entry, or a bundler that emitted chunk paths the manifest does not expose.","triggerScenarios":"Extension loaded on a JSON page whose CSP forbids script-src from the extension origin or blob:; extension build missing a chunk file; jsoncrack-react not bundled correctly; manifest web_accessible_resources not exposing the dynamic chunks.","commonSituations":"Corporate/internal sites with strict CSP; locally loaded file:// JSON; bundler emitting chunk paths the manifest does not expose; version skew between jsoncrack-react and the bundler config.","solutions":["Ensure all dynamically imported chunks are listed in manifest.json web_accessible_resources.","Open DevTools on the host page and read the CSP/blocked-URL directive in the console.","Bundle jsoncrack-react into a single file or load assets from the extension's own origin.","Log the full rejection (including non-Error values) so unknown shapes are diagnosable."],"exampleFix":"// before\n.catch((error: unknown) => {\n  if (!active) return;\n  const message = error instanceof Error ? error.message : \"Unable to load graph renderer.\";\n  setComponentError(message);\n});\n\n// after\n.catch((error: unknown) => {\n  if (!active) return;\n  console.error(\"loadJsonCrackComponent rejected:\", error);\n  const message = error instanceof Error ? error.message : \"Unable to load graph renderer.\";\n  setComponentError(message);\n});","handlingStrategy":"try-catch","validationCode":"// Verify the host page CSP allows the extension origin before importing\nfunction cspAllowsExtensionOrigin(): boolean {\n  const meta = document.querySelector('meta[http-equiv=\"Content-Security-Policy\"]')?.getAttribute(\"content\") ?? \"\";\n  const header = \"\"; // script-src from headers is not readable from JS; rely on console errors instead\n  return !/script-src[^;]*'none'/.test(meta);\n}","typeGuard":"function isJsonCrackModule(mod: unknown): mod is { JSONCrack: JSONCrackComponentType } {\n  return typeof (mod as any)?.JSONCrack === \"function\" || typeof (mod as any)?.JSONCrack === \"object\";\n}","tryCatchPattern":"loadJsonCrackComponent()\n  .then(component => { if (active) setJSONCrackComponent(() => component); })\n  .catch((error: unknown) => {\n    if (!active) return;\n    console.error(\"loadJsonCrackComponent rejected:\", error);\n    const message = error instanceof Error ? error.message : \"Unable to load graph renderer.\";\n    setComponentError(message);\n  });","preventionTips":["List every dynamically imported chunk in manifest.json web_accessible_resources.","Bundle jsoncrack-react into a single file when possible to avoid chunk-load failures under strict CSP.","Log the full rejection (including non-Error values) so unknown failure shapes are diagnosable.","Test the extension on a site with a strict default-src 'none' CSP before release."],"tags":["chrome-extension","csp","dynamic-import","wasm","content-script"],"backgroundTag":null,"analyzedSha":"3c9af69e23c635356293b6b28cf4cd0af10d1059","analyzedAt":"2026-08-12T19:00:27.891Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}