windmill-labs/windmill · error

Node ${moduleId} not found

Error message

Node ${moduleId} not found

What it means

addBranch appends an empty branch to a branchone/branchall step in the flow store. It locates the step by id via findModuleInFlow over the whole flow tree; if no module with that id exists it throws `Node ${moduleId} not found` (after already pushing an undo history entry). It indicates the target step id does not refer to any node currently in the flow.

Source

Thrown at frontend/src/lib/components/flows/branchOps.ts:22

import type { StateStore } from '$lib/utils'
import type { History } from '$lib/history.svelte'
import { push } from '$lib/history.svelte'
import { dfs } from './dfs'
import { findModuleInFlow } from './flowTree'

type BranchList = Array<{ summary?: string; expr?: string; modules: FlowModule[] }>

type Ctx = {
	flowStore: StateStore<ExtendedOpenFlow>
	flowStateStore: StateStore<FlowState>
	history: History<ExtendedOpenFlow>
}

/** Append an empty branch to a branchone/branchall step. */
export function addBranch(moduleId: string, { flowStore, history }: Omit<Ctx, 'flowStateStore'>) {
	push(history, flowStore.val)
	const module = findModuleInFlow(flowStore.val.value, moduleId)
	if (!module) throw new Error(`Node ${moduleId} not found`)

	if (module.value.type === 'branchone' || module.value.type === 'branchall') {
		module.value.branches.push({ summary: '', expr: 'false', modules: [] })
	}
}

/**
 * Drop a branch and the flow state of every step inside it.
 *
 * `index` counts the way the graph lays the branches out, where a branchone's default
 * occupies slot 0 — one ahead of the same branch's position in `value.branches`. Callers
 * working from the array (the settings panel) must add that offset back.
 */
export function removeBranch(
	moduleId: string,
	index: number,
	{ flowStore, flowStateStore, history }: Ctx
) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Verify the moduleId exists in the current flow (findModuleInFlow / flow outline) before calling addBranch.
  2. Strip any prefix from the id before lookup if the id came from a tracked action (see DUPLICATE_MODULE_PREFIX handling in flowDiffManager).
  3. Re-fetch/reload the flow if it changed remotely, then retry with the fresh id.
  4. If triggering from UI, capture the id at event time from the live module object rather than stale state.

Example fix

// before
addBranch(moduleId, ctx)
// after
import { findModuleInFlow } from './flowTree'
if (!findModuleInFlow($flowStore.val.value, moduleId)) return
addBranch(moduleId, ctx)
Defensive patterns

Strategy: validation

Validate before calling

import { findModuleInFlow } from './flowTree'
if (!findModuleInFlow(flowStore.val.value, moduleId)) return // unknown node

Type guard

const module = findModuleInFlow(flow.val.value, moduleId)
if (!module) return
if (module.value.type !== 'branchone' && module.value.type !== 'branchall') return

Try / catch

try { addBranch(moduleId, ctx) } catch (e) { if (String((e as Error).message).startsWith('Node ')) reloadFlowAndNotify(); else throw e }

Prevention

When it happens

Trigger: Calling addBranch(moduleId, ctx) with a stale/renamed/deleted module id, an id that includes a duplicate prefix (e.g. from the diff manager) that findModuleInFlow can't match, or an id belonging to a different flow than the one in flowStore.

Common situations: UI settings panel acting on a node that was just removed by another tab/collaborator (multiplayer); id normalization mismatches after copy/paste or import; dev code reusing cached ids across flow loads.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/c093d4f9c9dfbeff. Report an issue: GitHub.