mermaid-js/mermaid · error · Error

C4: no shape handler for "${node.shape}"

Error message

C4: no shape handler for "${node.shape}"

What it means

Thrown by the C4 renderer's shapeHandlerFor helper when a built C4 node's `shape` is falsy or has no entry in the unified `shapes` registry. The renderer looks up `shapes[node.shape]`; a miss means the C4 diagram produced a shape type the renderer cannot draw. This is generally an internal/version-mismatch issue rather than a user DSL error, since the C4 grammar only emits known shape keys.

Source

Thrown at packages/mermaid/src/diagrams/c4/c4Renderer.ts:279

  currentBounds: Bounds,
  diagram: SVG,
  c4ShapeArray: C4Shape[],
  c4ShapeKeys: string[]
) {
  const mermaidConfig = getConfig();
  const look = mermaidConfig.look ?? 'classic';
  const renderOptions = { config: mermaidConfig };
  // Namespace the shape DOM ids with the diagram id so two diagrams on one page don't
  // collide (the unified shapes use node.domId for the element id).
  const diagramId = diagram.attr('id') ?? '';

  // `c4ShapeKeys` are the (numeric string) indices of `c4ShapeArray`.
  const c4Shapes = c4ShapeKeys.map((key) => c4ShapeArray[Number(key)]);

  const shapeHandlerFor = (node: ReturnType<typeof buildC4Node>) => {
    const handler = node.shape ? shapes[node.shape] : undefined;
    if (!handler) {
      throw new Error(`C4: no shape handler for "${node.shape}"`);
    }
    return handler;
  };

  // Pass 1 (measure): render each shape, read its self-sized dimensions onto the legacy
  // c4Shape, then discard the rendering. The shape is drawn again in pass 2 directly at its
  // final position, so its label (and any composited sub-element) lays out under the final
  // transform - drawing at the origin and translating afterwards leaves composited layers
  // (e.g. anything with opacity) painting at the stale origin.
  await Promise.all(
    c4Shapes.map(async (c4Shape) => {
      const node = buildC4Node(c4Shape, conf, conf.c4ShapePadding, look, conf.width);
      node.domId = `${diagramId}-${node.id}`;
      const measured = await shapeHandlerFor(node)(diagram, node, renderOptions);
      c4Shape.width = node.width ?? conf.width;
      c4Shape.height = node.height ?? conf.height;
      c4Shape.margin = conf.c4ShapeMargin;
      measured.remove();

View on GitHub (pinned to d93e9c88c0)

Solutions

  1. Upgrade mermaid to a consistent version where c4Renderer and the shapes registry agree.
  2. Remove any custom/experimental C4 shape types from the diagram and use the built-in types.
  3. If it reproduces on the latest version with a standard C4 diagram, report it as a bug — the message is intended for exactly that case.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await mermaid.render(id, c4DiagramText);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('C4: no shape handler')) {
    // Unsupported C4 shape for this mermaid version — simplify the diagram or upgrade.
  }
  throw err;
}

Prevention

When it happens

Trigger: A C4 diagram where buildC4Node returns a shape string that is not a key in the shapes registry — e.g. due to a custom or experimental shape, or a shape key renamed/removed in a version transition where the c4 DB and the shapes registry disagree.

Common situations: Mermaid version mismatch where c4Renderer expects a shape the installed shapes module doesn't register; third-party extensions adding C4 types without a render handler; partial upgrades.

Related errors


AI-assisted analysis of mermaid-js/mermaid@d93e9c88c0 (2026-08-12). Data as JSON: /api/errors/f7152a0f9a4d82db. Report an issue: GitHub.