iOfficeAI/OfficeCLI · error · InvalidOperationException
mmdc failed (exit {p.ExitCode}). {msg}
Error message
mmdc failed (exit {p.ExitCode}). {msg} What it means
mmdc exited non-zero (or wrote no output file) but the captured stderr+stdout did NOT match any syntax-error signature, so the failure is classified as infrastructure rather than bad input. The exit code and raw mmdc output are embedded in the message. Unlike a syntax error, this does not fall back silently — it is a hard failure.
Source
Thrown at src/officecli/Core/Diagram/MermaidImageRenderer.cs:339
// interlocked when mmdc filled the stdout pipe first (bounded by
// the 120s kill below, but a wasted two minutes per diagram).
var errTask = p.StandardError.ReadToEndAsync();
var outTask = p.StandardOutput.ReadToEndAsync();
if (!p.WaitForExit(120_000))
{
try { p.Kill(true); } catch { /* best effort */ }
throw new InvalidOperationException("mmdc timed out after 120s.");
}
if (p.ExitCode != 0 || !File.Exists(outPath))
{
var msg = $"{errTask.Result}{outTask.Result}".Trim();
// A parse/unknown-type failure is bad input, not a broken mmdc; class
// it as syntax so the Add path surfaces it (and does not fall back).
if (LooksLikeSyntaxError(msg))
throw new MermaidSyntaxException(
$"mermaid syntax error: {msg} "
+ "(fix the mermaid source, or use render=native for the built-in subset).");
throw new InvalidOperationException($"mmdc failed (exit {p.ExitCode}). {msg}".Trim());
}
return outPath;
}
finally { try { File.Delete(inPath); } catch { /* best effort */ } }
}
// ----- chrome-family browser (mermaid.js in a page → sized screenshot) --------------
/// <summary>Two chrome passes: dump the DOM to read the diagram's viewBox, then
/// screenshot at exactly that size (HiDPI). PNG bakes in the browser's rendering
/// so mermaid's foreignObject labels — invisible to Office as SVG — appear.</summary>
private static string RenderViaChrome(string mermaid, string? background)
{
// A styled source (theme/layout/look frontmatter) is rendered by the ESM
// build (the UMD global does not render frontmatter, and elk is ESM-only);
// a plain source keeps the offline-cached UMD path unchanged.
var html = SourceNeedsEsm(mermaid)
? BuildHtmlEsm(mermaid, SourceNeedsElk(mermaid), SafeBackground(background))View on GitHub (pinned to 1ced45e900)
Solutions
- Run the exact mmdc command shown in the message manually to see the real chrome/puppeteer error.
- Ensure puppeteer's chromium is installed and chrome runtime libraries are present (`npx puppeteer browsers install chrome`).
- Point OFFICECLI_MMDC at a known-good mmdc and check `mmdc --version` / node version compatibility.
- If chrome cannot be made to work in the environment, use render=native to avoid the external process.
- Free memory/CPU on the host if chrome is being OOM-killed.
Example fix
// before
var png = MermaidImageRenderer.Render(mermaid, bg);
// after — probe mmdc health first, surface a clear error, optional native fallback
if (string.IsNullOrEmpty(MermaidImageRenderer.ProbeMmdc()))
throw new InvalidOperationException("mmdc not found; install @mermaid-js/mermaid-cli or set OFFICECLI_MMDC.");
try { var png = MermaidImageRenderer.Render(mermaid, bg); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("mmdc failed")) {
// chrome/puppeteer broken in this env — fall back to the built-in native subset
var png = MermaidImageRenderer.RenderNative(mermaid, bg);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Probe mmdc health on a trivial diagram once at startup
static bool MmdcHealthy(string mmdc)
{
try
{
var psi = new ProcessStartInfo(mmdc, "-i <trivial.mmd> -o /tmp/probe.png");
using var p = Process.Start(psi);
return p != null && p.WaitForExit(30_000) && p.ExitCode == 0;
}
catch { return false; }
} Try / catch
try { png = MermaidImageRenderer.Render(mermaid, bg); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("mmdc failed", StringComparison.Ordinal))
{
// infra failure (chrome/puppeteer) — fall back to native subset or report
png = MermaidImageRenderer.RenderNative(mermaid, bg);
} Prevention
- Ensure puppeteer's chromium and chrome runtime libs are installed in every environment.
- Pin a known-good @mermaid-js/mermaid-cli version.
- Prefer render=native in minimal/CI containers where chrome is fragile.
When it happens
Trigger: mmdc crashes or refuses to run for a non-syntax reason: puppeteer/chrome binary missing or misconfigured, a port conflict, an out-of-memory kill of chrome, an incompatible node version, a broken global mmdc install, or mmdc writing its PNG to an unexpected path so File.Exists(outPath) is false.
Common situations: Puppeteer's bundled chromium not installed (`npx puppeteer install`); running on a stripped-down container missing chrome runtime libs; node version mismatch with @mermaid-js/mermaid-cli; OUT_OF_MEMORY in CI killing chrome; mmdc global install corrupted.
Related errors
- mmdc timed out after 120s.
- failed to start mmdc.
- headless browser produced no output.
- mermaid failed to render: {ExtractMermaidMessage(dom)}
- mermaid produced no diagram (mermaid.js failed to load or th
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/5f7a129ef83bd735.
Report an issue: GitHub.