iOfficeAI/OfficeCLI · error · InvalidOperationException
mermaid produced no diagram (mermaid.js failed to load or th
Error message
mermaid produced no diagram (mermaid.js failed to load or the render timed out).
What it means
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.
Source
Thrown at src/officecli/Core/Diagram/MermaidImageRenderer.cs:377
var htmlPath = Path.Combine(Path.GetTempPath(), $"ocli_mmd_{Guid.NewGuid():N}.html");
File.WriteAllText(htmlPath, html);
try
{
var dom = HtmlScreenshot.DumpDom(htmlPath)
?? throw new InvalidOperationException("headless browser produced no output.");
// The <title> is the AUTHORITATIVE outcome — not the presence of an <svg>.
// On a syntax error mermaid.parse() rejects (we capture the message) but
// STILL injects its red "Syntax error" bomb graphic into the DOM anyway
// (suppressErrorRendering doesn't stop it). So a viewBox is present even on
// failure; keying off the svg would screenshot the bomb and "succeed".
// Trust the title: MMDREADY = real render, MMDSYNTAX = bad input, else infra.
if (dom.Contains("<title>MMDSYNTAX</title>", StringComparison.Ordinal))
throw new MermaidSyntaxException(
"mermaid syntax error: " + ExtractMermaidMessage(dom)
+ "\n(fix the mermaid source, or use render=native for the built-in subset).");
if (!dom.Contains("<title>MMDREADY</title>", StringComparison.Ordinal))
throw new InvalidOperationException(
dom.Contains("<title>MMDERR</title>", StringComparison.Ordinal)
? "mermaid failed to render: " + ExtractMermaidMessage(dom)
: "mermaid produced no diagram (mermaid.js failed to load or the render timed out).");
var (w, h) = ParseSvgSize(dom);
if (w <= 0 || h <= 0)
throw new InvalidOperationException("mermaid rendered but produced no measurable svg viewBox.");
var pngPath = Path.ChangeExtension(htmlPath, ".png");
if (!HtmlScreenshot.CaptureChromeSized(htmlPath, pngPath,
(int)Math.Ceiling(w) + 2, (int)Math.Ceiling(h) + 2))
throw new InvalidOperationException("headless screenshot failed.");
return pngPath;
}
finally { try { File.Delete(htmlPath); } catch { /* best effort */ } }
}
/// <summary>Cache → one-time download → live CDN. Returns a URL usable as aView on GitHub (pinned to 1ced45e900)
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.
Example fix
// before — relies on CDN at runtime, fails offline
var png = MermaidImageRenderer.RenderViaChrome(mermaid, bg);
// after — seed the cache once so the browser path is offline-safe
if (!File.Exists(CachedJsPath) || new FileInfo(CachedJsPath).Length <= 500_000)
File.WriteAllBytes(CachedJsPath, DownloadMermaidJs());
var png = MermaidImageRenderer.RenderViaChrome(mermaid, bg); Defensive patterns
Strategy: retry
Validate before calling
// Ensure an offline-safe mermaid.js cache before relying on the browser path
if (!File.Exists(CachedJsPath) || new FileInfo(CachedJsPath).Length <= 500_000)
File.WriteAllBytes(CachedJsPath, DownloadMermaidJsWithTimeout()); Try / catch
for (int attempt = 0; attempt < 2; attempt++)
{
try { return MermaidImageRenderer.RenderViaChrome(mermaid, bg); }
catch (InvalidOperationException ex) when (ex.Message.Contains("failed to load or the render timed out", StringComparison.Ordinal) && attempt == 0)
{ /* transient CDN — retry once */ }
}
return MermaidImageRenderer.RenderNative(mermaid, bg); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- mmdc failed (exit {p.ExitCode}). {msg}
- headless browser produced no output.
- mermaid failed to render: {ExtractMermaidMessage(dom)}
- headless screenshot failed.
- failed to start mmdc.
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/1841e47d6433c7ad.
Report an issue: GitHub.