mermaid-js/mermaid · error

State not found

Error message

State not found: ${trimmedId}

What it means

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.

Solutions

  1. Give each render its own db instance (or call clear() before each parse) so the map is not shared.
  2. Avoid mutating the diagram db asynchronously while a parse is in flight.
  3. Audit custom parser hooks for any state deletion during addState.
  4. Reproduce in isolation; if it persists with a fresh db, report as a parser bug.

Example fix

// before — shared db reused concurrently
mermaid.render(d1); mermaid.render(d2); // races on stateDb

// after — isolate per render
await mermaid.render(id1, d1);
await mermaid.render(id2, d2);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the state exists before the second lookup style guards expect it
if (!stateDb.getDocument().states.has(trimmedId)) {
  throw new Error(`State ${trimmedId} must be declared first`);
}

Type guard

const isStateNotFound = (e): boolean =>
  e instanceof Error && /^State not found:/.test(e.message);

Try / catch

try {
  await mermaid.run({ nodes: [el] });
} catch (e) {
  if (e instanceof Error && /^State not found:/.test(e.message)) {
    // this is an internal invariant; isolate the db per render and retry
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at packages/mermaid/src/diagrams/state/stateDb.ts:414

  ) {
    const trimmedId = id?.trim();
    if (!this.currentDocument.states.has(trimmedId)) {
      log.info('Adding state ', trimmedId, descr);
      this.currentDocument.states.set(trimmedId, {
        stmt: STMT_STATE,
        id: trimmedId,
        descriptions: [],
        type,
        doc,
        note,
        classes: [],
        styles: [],
        textStyles: [],
      });
    } else {
      const state = this.currentDocument.states.get(trimmedId);
      if (!state) {
        throw new Error(`State not found: ${trimmedId}`);
      }
      if (!state.doc) {
        state.doc = doc;
      }
      if (!state.type) {
        state.type = type;
      }
    }

    if (descr) {
      log.info('Setting state description', trimmedId, descr);
      const descriptions = Array.isArray(descr) ? descr : [descr];
      descriptions.forEach((des) => this.addDescription(trimmedId, des.trim()));
    }

    if (note) {
      const doc2 = this.currentDocument.states.get(trimmedId);
      if (!doc2) {

View on GitHub (pinned to d93e9c88c0)