mermaid-js/mermaid · error · Error

union requires multiple identifiers

Error message

union requires multiple identifiers

What it means

Thrown by the venn.jison grammar production 'UNION identifierList' (line 77) when the reduced identifierList has length < 2. In a venn-beta diagram 'union' denotes the intersection/combination region of two or more sets, so a single identifier is semantically meaningless and the grammar rejects it at reduction time before addSubsetData runs. The same guard is duplicated across the three other union productions (lines 78-80); this variant is the bare 'union A' form with no label or size.

Source

Thrown at packages/mermaid/src/diagrams/venn/parser/venn.jison:77

  ;

document
  : /* empty */         { $$ = [] }
  | document line       { $1.push($2); $$ = $1 }
  ;

line
  : NEWLINE { $$ = []; }
  | statement { $$ = $1; }
  ;

statement
  : TITLE                                            { yy.setDiagramTitle($1.substr(6)); $$ = $1.substr(6); }
  | SET identifier                                   { yy.addSubsetData([$identifier], undefined, undefined); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | SET identifier BRACKET_LABEL                     { yy.addSubsetData([$identifier], $BRACKET_LABEL, undefined); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | SET identifier COLON NUMERIC                     { yy.addSubsetData([$identifier], undefined, parseFloat($NUMERIC)); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | SET identifier BRACKET_LABEL COLON NUMERIC       { yy.addSubsetData([$identifier], $BRACKET_LABEL, parseFloat($NUMERIC)); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | UNION identifierList                             { if ($identifierList.length < 2) { throw new Error('union requires multiple identifiers'); } if (yy.validateUnionIdentifiers) { yy.validateUnionIdentifiers($identifierList); } yy.addSubsetData($identifierList, undefined, undefined); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | UNION identifierList BRACKET_LABEL               { if ($identifierList.length < 2) { throw new Error('union requires multiple identifiers'); } if (yy.validateUnionIdentifiers) { yy.validateUnionIdentifiers($identifierList); } yy.addSubsetData($identifierList, $BRACKET_LABEL, undefined); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | UNION identifierList COLON NUMERIC               { if ($identifierList.length < 2) { throw new Error('union requires multiple identifiers'); } if (yy.validateUnionIdentifiers) { yy.validateUnionIdentifiers($identifierList); } yy.addSubsetData($identifierList, undefined, parseFloat($NUMERIC)); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | UNION identifierList BRACKET_LABEL COLON NUMERIC { if ($identifierList.length < 2) { throw new Error('union requires multiple identifiers'); } if (yy.validateUnionIdentifiers) { yy.validateUnionIdentifiers($identifierList); } yy.addSubsetData($identifierList, $BRACKET_LABEL, parseFloat($NUMERIC)); if (yy.setIndentMode) { yy.setIndentMode(true); } }
  | TEXT identifierList IDENTIFIER                     { yy.addTextData($identifierList, $IDENTIFIER, undefined); }
  | TEXT identifierList STRING                          { yy.addTextData($identifierList, $STRING, undefined); }
  | TEXT identifierList NUMERIC                         { yy.addTextData($identifierList, $NUMERIC, undefined); }
  | TEXT identifierList IDENTIFIER BRACKET_LABEL        { yy.addTextData($identifierList, $IDENTIFIER, $BRACKET_LABEL); }
  | TEXT identifierList STRING BRACKET_LABEL            { yy.addTextData($identifierList, $STRING, $BRACKET_LABEL); }
  | INDENT_TEXT indentedTextTail                     { $$ = $2; }
  | STYLE identifierList stylesOpt                   { yy.addStyleData($identifierList, $stylesOpt); }
  ;

indentedTextTail
  : IDENTIFIER               { var cs = yy.getCurrentSets(); if (!cs) throw new Error('text requires set'); yy.addTextData(cs, $IDENTIFIER, undefined); }
  | STRING                   { var cs = yy.getCurrentSets(); if (!cs) throw new Error('text requires set'); yy.addTextData(cs, $STRING, undefined); }
  | NUMERIC                  { var cs = yy.getCurrentSets(); if (!cs) throw new Error('text requires set'); yy.addTextData(cs, $NUMERIC, undefined); }
  | IDENTIFIER BRACKET_LABEL { var cs = yy.getCurrentSets(); if (!cs) throw new Error('text requires set'); yy.addTextData(cs, $IDENTIFIER, $BRACKET_LABEL); }
  | STRING BRACKET_LABEL     { var cs = yy.getCurrentSets(); if (!cs) throw new Error('text requires set'); yy.addTextData(cs, $STRING, $BRACKET_LABEL); }

View on GitHub (pinned to d93e9c88c0)

Solutions

  1. Add at least one more identifier: 'union A,B'
  2. If only one set is intended, use 'set A' instead of 'union A'
  3. Declare every referenced set with 'set' first so the subsequent validateUnionIdentifiers check (vennDB.ts:80) also passes

Example fix

// before
venn-beta
  set A
  union A

// after
venn-beta
  set A
  set B
  union A,B
Defensive patterns

Strategy: validation

Validate before calling

function validateUnionLines(src) {
  for (const line of src.split('\n')) {
    const m = line.match(/^\s*union\s+(.+?)(?:\s*(?:\[|:|$))/i);
    if (!m) continue;
    const ids = m[1].split(',').map(s => s.trim().replace(/^"|"$/g, '')).filter(Boolean);
    if (ids.length < 2) throw new Error('union requires multiple identifiers: ' + line.trim());
  }
}

Type guard

function isUnionCountError(e) { return e instanceof Error && /union requires multiple identifiers/.test(e.message); }

Try / catch

try {
  venn.parse(src);
} catch (e) {
  if (e instanceof Error && /union requires multiple identifiers/.test(e.message)) {
    // tell the user each union needs >= 2 sets
  } else throw e;
}

Prevention

When it happens

Trigger: A venn-beta source containing a union statement that lists only one set identifier, e.g. 'union A'. The identifierList non-terminal (venn.jison:125-128) reduces to a single element, so the inline guard 'if ($identifierList.length < 2) throw' fires.

Common situations: Forgetting the comma-separated second set; using 'union' where 'set' was intended; dropping the second id while editing; assuming union accepts a single set like set does.

Related errors


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