garrytan/gstack · error

invalid mermaid render id: ${id}

Error message

invalid mermaid render id: ${id}

What it means

Thrown by __renderMermaid() when the caller-supplied diagram id fails the regex ^[A-Za-z][\w-]*$. Mermaid bakes the id into internal SVG element ids (gradients, markers, clip paths), so an invalid id would produce malformed SVG or id collisions between two diagrams inlined into one document. The guard enforces: starts with a letter, remaining chars are word characters or hyphens. This is a hard precondition before mermaid.render is ever called.

Source

Thrown at lib/diagram-render/src/entry.ts:67

// Font stacks must match make-pdf/src/print-css.ts (sans + CJK + emoji) so
// mermaid's text measurement in this tab matches the print document's layout.
const PRINT_SANS =
  'Helvetica, "Liberation Sans", Arial, "Hiragino Kaku Gothic ProN", ' +
  '"Noto Sans CJK JP", "Microsoft YaHei", "Apple Color Emoji", ' +
  '"Segoe UI Emoji", "Noto Color Emoji", sans-serif';

mermaid.initialize({
  startOnLoad: false,
  securityLevel: "strict",
  theme: "neutral",
  fontFamily: PRINT_SANS,
  htmlLabels: false,
  flowchart: { htmlLabels: false },
});

window.__renderMermaid = async (id: string, text: string): Promise<string> => {
  if (!/^[A-Za-z][\w-]*$/.test(id)) throw new Error(`invalid mermaid render id: ${id}`);
  const { svg } = await mermaid.render(id, text);
  return svg;
};

window.__mermaidToExcalidraw = async (text: string): Promise<string> => {
  const { elements, files } = await parseMermaidToExcalidraw(text);
  const converted = convertToExcalidrawElements(elements);
  const scene = {
    type: "excalidraw",
    version: 2,
    source: "gstack-diagram-render",
    elements: converted,
    appState: { viewBackgroundColor: "#ffffff" },
    files: files ?? {},
  };
  return JSON.stringify(scene);
};

View on GitHub (pinned to 94993f7401)

Solutions

  1. Prefix the id with a letter, e.g. 'mermaid-fence-' + index.
  2. Strip or replace any non-[A-Za-z0-9_-] characters before calling.
  3. Ensure the id is non-empty and starts with [A-Za-z].
  4. If auto-generating, use a fixed prefix like 'd' + counter.

Example fix

// before
__renderMermaid('1-flow', graph);

// after
__renderMermaid('mermaid-fence-1', graph);
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeMermaidId(id: string): string {
  let cleaned = id.replace(/[^\w-]/g, '');
  if (!/^[A-Za-z]/.test(cleaned)) cleaned = 'd' + cleaned;
  return cleaned || 'diagram';
}

Type guard

function isValidMermaidId(id: string): boolean {
  return typeof id === 'string' && /^[A-Za-z][\w-]*$/.test(id);
}

Prevention

When it happens

Trigger: Caller invokes window.__renderMermaid(id, text) with an id that starts with a digit, contains spaces/slashes/colons, is empty, or includes unicode. Typical orchestrator ids are 'mermaid-fence-<n>' which always pass.

Common situations: Orchestrator bug generating ids from raw fence indices without a letter prefix; user-supplied diagram name used directly as id; id derived from a file path containing slashes; an id that worked in an older mermaid but now fails stricter validation.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/a19fa00a37aaf526. Report an issue: GitHub.