{"record":{"id":"1a9181c46b3d673f","repo":"mermaid-js/mermaid","slug":"error-state-name-must-be-a-single-word-found","errorCode":null,"errorMessage":"Error: State name must be a single word. Found: \"${yytext.trim()}\"","messagePattern":"Error: State name must be a single word\\. Found: \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mermaid/src/diagrams/state/parser/stateDiagram.jison","lineNumber":133,"sourceCode":"\n<STATE>.*\"<<fork>>\"                   {this.popState();yytext=yytext.slice(0,-8).trim(); /*console.warn('Fork Fork: ',yytext);*/return 'FORK';}\n<STATE>.*\"<<join>>\"                   {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}\n<STATE>.*\"<<choice>>\"                 {this.popState();yytext=yytext.slice(0,-10).trim();/*console.warn('Fork Join: ',yytext);*/return 'CHOICE';}\n<STATE>.*\"[[fork]]\"                   {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Fork: ',yytext);*/return 'FORK';}\n<STATE>.*\"[[join]]\"                   {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}\n<STATE>.*\"[[choice]]\"                 {this.popState();yytext=yytext.slice(0,-10).trim();/*console.warn('Fork Join: ',yytext);*/return 'CHOICE';}\n\n<struct>.*direction\\s+TB[^\\n]*            { return 'direction_tb';}\n<struct>.*direction\\s+BT[^\\n]*            { return 'direction_bt';}\n<struct>.*direction\\s+RL[^\\n]*            { return 'direction_rl';}\n<struct>.*direction\\s+LR[^\\n]*            { return 'direction_lr';}\n\n<STATE>[\"]                 { /* console.log('Starting STATE_STRING'); */ this.pushState(\"STATE_STRING\"); }\n<STATE>\\s*\"as\"\\s+          { this.pushState('STATE_ID'); /* console.log('pushState(STATE_ID)'); */ return \"AS\"; }\n<STATE_ID>[^\\n\\{]*         { if (!processId()) return; this.popState(); /* console.log('STATE_ID', yytext); */ return \"ID\"; }\n<STATE_STRING>[\"]          { this.popState(); }\n<STATE_STRING>[^\"]*        { /* console.log('Long description:', yytext); */ return \"STATE_DESCR\"; }\n<STATE>\\w+\\s+\\w+.*?\\{      { throw new Error('Error: State name must be a single word. Found: \"' + yytext.trim() + '\"'); }\n<STATE>[^\\n\\s\\{]+          { /* console.log('COMPOSIT_STATE', yytext); */ return 'COMPOSIT_STATE'; }\n<STATE>\\n                  { this.popState(); }\n<INITIAL,STATE>\\{          { this.popState(); this.pushState('struct'); /* console.log('begin struct', yytext); */ return 'STRUCT_START'; }\n<struct>\\}                 { /*console.log('Ending struct');*/ this.popState(); return 'STRUCT_STOP';} }\n<struct>[\\n]               /* nothing */\n\n<INITIAL,struct>\"note\"\\s+           { this.begin('NOTE'); return 'note'; }\n<NOTE>\"left of\"                     { this.popState(); this.pushState('NOTE_ID'); return 'left_of'; }\n<NOTE>\"right of\"                    { this.popState(); this.pushState('NOTE_ID'); return 'right_of'; }\n<NOTE>\\\"                            { this.popState(); this.pushState('FLOATING_NOTE'); }\n<FLOATING_NOTE>\\s*\"as\"\\s*           { this.popState(); this.pushState('FLOATING_NOTE_ID'); return \"AS\"; }\n<FLOATING_NOTE>[\"]                  /**/\n<FLOATING_NOTE>[^\"]*                { /* console.log('Floating note text: ', yytext); */ return \"NOTE_TEXT\"; }\n<FLOATING_NOTE_ID>[^\\n]*            { if (!processId()) return; this.popState(); /* console.log('Floating note ID', yytext);*/ return \"ID\"; }\n<NOTE_ID>\\s*[^:\\n\\s\\-]+             { if (!processId()) return; this.popState(); this.pushState('NOTE_TEXT'); /*console.log('Got ID for note', yytext);*/ return 'ID'; }\n<NOTE_TEXT>\\s*\":\"[^:\\n;]+           { this.popState(); /* console.log('Got NOTE_TEXT for note',yytext);*/yytext = yytext.substr(2).trim(); return 'NOTE_TEXT'; }\n<NOTE_TEXT>[\\s\\S]*?\\n\\s*\"end note\"  { this.popState(); /* console.log('Got NOTE_TEXT for note',yytext);*/yytext = yytext.slice(0,-8).trim(); return 'NOTE_TEXT'; }\n","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/mermaid-js/mermaid/blob/d93e9c88c01a599c062ee6a3f1462e3558ac6b90/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison#L115-L151","documentation":"Thrown at stateDiagram.jison:133 by the lexer rule <STATE>\\w+\\s+\\w+.*?\\{ while the lexer is in the STATE state (pushed by the 'state' keyword rule at stateDiagram.jison:114). The rule matches a word, whitespace, another word, then anything up to an opening brace and rejects it, because a composite state (one with a {...} body) must use either a single-word identifier or a quoted label. A bare multi-word name before the brace is ambiguous with the ID/DESCR transitions and is therefore disallowed. The thrown value is a plain Error whose message echoes the offending yytext.","triggerScenarios":"A stateDiagram or stateDiagram-v2 source containing a composite state whose name has a space and is neither quoted nor given an 'as' alias, e.g. 'state Foo Bar { ... }'. The lexer is in STATE mode when it matches word-space-word-...-{ and throws immediately during lexing, before the grammar reduces.","commonSituations":"Authoring a nested/composite state with a descriptive multi-word label and forgetting the quotes; assuming the simple-ID rule allows spaces; migrating from another tool's syntax that permitted spaces in state names; confusing the display label with the internal identifier.","solutions":["Quote the multi-word label: state \"Foo Bar\" { ... }","Use an explicit alias: state \"Foo Bar\" as FB { ... }","Use a single-word identifier with no space: state FooBar { ... }"],"exampleFix":"// before\nstateDiagram-v2\n  state Foo Bar {\n    [*] --> Bar\n  }\n\n// after\nstateDiagram-v2\n  state \"Foo Bar\" {\n    [*] --> Bar\n  }","handlingStrategy":"validation","validationCode":"// Catches the 'state <word> <word> ... {' shape before the lexer throws.\nfunction validateCompositeStateNames(src) {\n  for (const line of src.split('\\n')) {\n    if (/^\\s*state\\s+\\w+\\s+\\w+.*\\{/.test(line)) {\n      throw new Error('Composite state name must be quoted or single-word: ' + line.trim());\n    }\n  }\n}","typeGuard":"function isStateNameError(e) { return e instanceof Error && /State name must be a single word/.test(e.message); }","tryCatchPattern":"try {\n  stateParser.parse(src);\n} catch (e) {\n  if (e instanceof Error && /State name must be a single word/.test(e.message)) {\n    // prompt the user to quote multi-word composite state names\n  } else throw e;\n}","preventionTips":["Always quote multi-word state labels, even for non-composite states, to stay safe across grammar revisions.","Prefer 'state \"Label\" as ID' so the display name and identifier are explicit.","Lint generated stateDiagram sources with the regex above before rendering.","When accepting diagram text from users, show the exact offending line from the error message."],"tags":["state-diagram","jison","lexer","syntax","mermaid"],"backgroundTag":null,"analyzedSha":"d93e9c88c01a599c062ee6a3f1462e3558ac6b90","analyzedAt":"2026-08-12T06:23:11.304Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}