{"record":{"id":"1841e47d6433c7ad","repo":"iOfficeAI/OfficeCLI","slug":"mermaid-produced-no-diagram-mermaid-js-failed-to","errorCode":null,"errorMessage":"mermaid produced no diagram (mermaid.js failed to load or the render timed out).","messagePattern":"mermaid produced no diagram \\(mermaid\\.js failed to load or the render timed out\\)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/Diagram/MermaidImageRenderer.cs","lineNumber":377,"sourceCode":"        var htmlPath = Path.Combine(Path.GetTempPath(), $\"ocli_mmd_{Guid.NewGuid():N}.html\");\n        File.WriteAllText(htmlPath, html);\n        try\n        {\n            var dom = HtmlScreenshot.DumpDom(htmlPath)\n                ?? throw new InvalidOperationException(\"headless browser produced no output.\");\n\n            // The <title> is the AUTHORITATIVE outcome — not the presence of an <svg>.\n            // On a syntax error mermaid.parse() rejects (we capture the message) but\n            // STILL injects its red \"Syntax error\" bomb graphic into the DOM anyway\n            // (suppressErrorRendering doesn't stop it). So a viewBox is present even on\n            // failure; keying off the svg would screenshot the bomb and \"succeed\".\n            // Trust the title: MMDREADY = real render, MMDSYNTAX = bad input, else infra.\n            if (dom.Contains(\"<title>MMDSYNTAX</title>\", StringComparison.Ordinal))\n                throw new MermaidSyntaxException(\n                    \"mermaid syntax error: \" + ExtractMermaidMessage(dom)\n                    + \"\\n(fix the mermaid source, or use render=native for the built-in subset).\");\n            if (!dom.Contains(\"<title>MMDREADY</title>\", StringComparison.Ordinal))\n                throw new InvalidOperationException(\n                    dom.Contains(\"<title>MMDERR</title>\", StringComparison.Ordinal)\n                    ? \"mermaid failed to render: \" + ExtractMermaidMessage(dom)\n                    : \"mermaid produced no diagram (mermaid.js failed to load or the render timed out).\");\n\n            var (w, h) = ParseSvgSize(dom);\n            if (w <= 0 || h <= 0)\n                throw new InvalidOperationException(\"mermaid rendered but produced no measurable svg viewBox.\");\n\n            var pngPath = Path.ChangeExtension(htmlPath, \".png\");\n            if (!HtmlScreenshot.CaptureChromeSized(htmlPath, pngPath,\n                    (int)Math.Ceiling(w) + 2, (int)Math.Ceiling(h) + 2))\n                throw new InvalidOperationException(\"headless screenshot failed.\");\n            return pngPath;\n        }\n        finally { try { File.Delete(htmlPath); } catch { /* best effort */ } }\n    }\n\n    /// <summary>Cache → one-time download → live CDN. Returns a URL usable as a","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/Diagram/MermaidImageRenderer.cs#L359-L395","documentation":"The DOM title was none of MMDREADY, MMDSYNTAX, or MMDSYNTAX's render sibling — it lacked MMDREADY entirely and was not MMDSYNTAX or MMDSYNTAX->ERR. With no recognized outcome marker, the failure is classified as infra: mermaid.js never finished, either because the script failed to load or the render timed out before the page could set the title.","triggerScenarios":"ResolveMermaidJsRef fell through cache → download → live CDN and the CDN was unreachable (offline, proxy block, 404), so mermaid never defined and the title was never set; or the page's render timed out inside DumpDom's wait window before mermaid finished.","commonSituations":"Air-gapped/offline machine with no cached mermaid.js and CDN unreachable; corporate proxy blocking the CDN; a transient network drop during the one-time download; the render genuinely exceeding DumpDom's internal wait.","solutions":["Pre-cache mermaid.js so ResolveMermaidJsRef doesn't depend on the network (place a >500KB copy at CachedJsPath).","Check network/proxy access to the CDN; allowlist the mermaid CDN host.","Retry once — a transient CDN failure is the most common cause.","Use render=native to avoid the browser/CDN path entirely."],"exampleFix":"// before — relies on CDN at runtime, fails offline\nvar png = MermaidImageRenderer.RenderViaChrome(mermaid, bg);\n\n// after — seed the cache once so the browser path is offline-safe\nif (!File.Exists(CachedJsPath) || new FileInfo(CachedJsPath).Length <= 500_000)\n    File.WriteAllBytes(CachedJsPath, DownloadMermaidJs());\nvar png = MermaidImageRenderer.RenderViaChrome(mermaid, bg);","handlingStrategy":"retry","validationCode":"// Ensure an offline-safe mermaid.js cache before relying on the browser path\nif (!File.Exists(CachedJsPath) || new FileInfo(CachedJsPath).Length <= 500_000)\n    File.WriteAllBytes(CachedJsPath, DownloadMermaidJsWithTimeout());","typeGuard":null,"tryCatchPattern":"for (int attempt = 0; attempt < 2; attempt++)\n{\n    try { return MermaidImageRenderer.RenderViaChrome(mermaid, bg); }\n    catch (InvalidOperationException ex) when (ex.Message.Contains(\"failed to load or the render timed out\", StringComparison.Ordinal) && attempt == 0)\n    { /* transient CDN — retry once */ }\n}\nreturn MermaidImageRenderer.RenderNative(mermaid, bg);","preventionTips":["Pre-cache mermaid.js so the browser path is offline-safe.","Allowlist the mermaid CDN behind corporate proxies.","Use render=native in air-gapped environments."],"tags":["mermaid","cdn","network","offline","chrome","infrastructure"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}