{"record":{"id":"39d0c0122a985f59","repo":"mermaid-js/mermaid","slug":"state-not-found-trimmedid","errorCode":null,"errorMessage":"State not found: ${trimmedId}","messagePattern":"State not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/mermaid/src/diagrams/state/stateDb.ts","lineNumber":414,"sourceCode":"  ) {\n    const trimmedId = id?.trim();\n    if (!this.currentDocument.states.has(trimmedId)) {\n      log.info('Adding state ', trimmedId, descr);\n      this.currentDocument.states.set(trimmedId, {\n        stmt: STMT_STATE,\n        id: trimmedId,\n        descriptions: [],\n        type,\n        doc,\n        note,\n        classes: [],\n        styles: [],\n        textStyles: [],\n      });\n    } else {\n      const state = this.currentDocument.states.get(trimmedId);\n      if (!state) {\n        throw new Error(`State not found: ${trimmedId}`);\n      }\n      if (!state.doc) {\n        state.doc = doc;\n      }\n      if (!state.type) {\n        state.type = type;\n      }\n    }\n\n    if (descr) {\n      log.info('Setting state description', trimmedId, descr);\n      const descriptions = Array.isArray(descr) ? descr : [descr];\n      descriptions.forEach((des) => this.addDescription(trimmedId, des.trim()));\n    }\n\n    if (note) {\n      const doc2 = this.currentDocument.states.get(trimmedId);\n      if (!doc2) {","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/mermaid-js/mermaid/blob/d93e9c88c01a599c062ee6a3f1462e3558ac6b90/packages/mermaid/src/diagrams/state/stateDb.ts#L396-L432","documentation":"Defensive guard inside addState(): in the branch where states.has(trimmedId) was true, the immediate states.get(trimmedId) is re-checked and throws if it returns undefined. In single-threaded use this is effectively unreachable (the map cannot lose an entry between has() and get()), so seeing it signals concurrent mutation of the internal state map or a corrupted/deleted entry mid-parse.","triggerScenarios":"The states Map is mutated from another context (shared db reused across diagrams without clear(), or async/worker access) so the entry present at has() is gone at get(); a custom parser extension that deletes states during traversal.","commonSituations":"Reusing a singleton stateDb across concurrently-rendered diagrams; clearing the db on one thread while another parses; forked/patched parser code that removes states; very rare race in an embedding host.","solutions":["Give each render its own db instance (or call clear() before each parse) so the map is not shared.","Avoid mutating the diagram db asynchronously while a parse is in flight.","Audit custom parser hooks for any state deletion during addState.","Reproduce in isolation; if it persists with a fresh db, report as a parser bug."],"exampleFix":"// before — shared db reused concurrently\nmermaid.render(d1); mermaid.render(d2); // races on stateDb\n\n// after — isolate per render\nawait mermaid.render(id1, d1);\nawait mermaid.render(id2, d2);","handlingStrategy":"validation","validationCode":"// Ensure the state exists before the second lookup style guards expect it\nif (!stateDb.getDocument().states.has(trimmedId)) {\n  throw new Error(`State ${trimmedId} must be declared first`);\n}","typeGuard":"const isStateNotFound = (e): boolean =>\n  e instanceof Error && /^State not found:/.test(e.message);","tryCatchPattern":"try {\n  await mermaid.run({ nodes: [el] });\n} catch (e) {\n  if (e instanceof Error && /^State not found:/.test(e.message)) {\n    // this is an internal invariant; isolate the db per render and retry\n  } else { throw e; }\n}","preventionTips":["Use a fresh/isolated db per render; call clear() before each parse.","Never mutate the db concurrently across diagrams.","Treat this error as a bug indicator and report if reproducible."],"tags":["state","internal-invariant","concurrency","diagram-db"],"backgroundTag":null,"analyzedSha":"d93e9c88c01a599c062ee6a3f1462e3558ac6b90","analyzedAt":"2026-08-12T06:23:11.304Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}