windmill-labs/windmill · error

Cannot reject module without a beforeFlow snapshot

Error message

Cannot reject module without a beforeFlow snapshot

What it means

rejectModule reverts one module-level change detected by the flow diff manager by restoring it from the `beforeFlow` snapshot taken when the diff session started. If no snapshot was captured (`beforeFlow` is unset) there is nothing to revert to, so it throws 'Cannot reject module without a beforeFlow snapshot'. This is an internal-state precondition: rejecting a diff is meaningless without the baseline.

Source

Thrown at frontend/src/lib/components/flows/flowDiffManager.svelte.ts:315

			}
		} else if (info.action === 'modified') {
			// Modified: Apply modifications to beforeFlow module
			const beforeModule = findModuleInFlow(beforeFlow.value, actualId) ?? undefined
			const afterModule = findModuleInFlow(currentFlow, actualId) ?? undefined

			if (beforeModule && afterModule) {
				replaceFlowModule(beforeModule, $state.snapshot(afterModule))
			}
		}
	}

	/**
	 * Reject a module action (revert the changes)
	 * Removes the action from tracking after rejection
	 */
	function rejectModule(id: string, flowStore?: StateStore<ExtendedOpenFlow>) {
		if (!beforeFlow) {
			throw new Error('Cannot reject module without a beforeFlow snapshot')
		}

		const actualId = id.startsWith(DUPLICATE_MODULE_PREFIX)
			? id.substring(DUPLICATE_MODULE_PREFIX.length)
			: id
		const info = moduleActions[id]

		if (!info) return

		// Only perform revert operations if flowStore is provided
		if (flowStore) {
			if (id === SPECIAL_MODULE_IDS.INPUT) {
				// Revert input schema changes
				flowStore.val.schema = beforeFlow.schema
				currentInputSchema = flowStore.val.schema
			} else if (info.action === 'added') {
				// Added in after: Remove from flowStore (currentFlow)
				// deleteModuleFromFlow handles the case where the module was already deleted (e.g., with its parent)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Only create/use the diff manager (and call rejectModule) within a review session that passes the original flow as beforeFlow.
  2. Disable the reject/reject-all UI when beforeFlow is absent.
  3. Re-open the review (recompute the diff with a fresh snapshot) if the session was already ended.
  4. Guard calls: check the manager exposes a snapshot/hasBeforeFlow indicator first.

Example fix

// before
rejectAll()
// after
if (!diffManager.hasBeforeFlow) return
diffManager.rejectAll()
Defensive patterns

Strategy: validation

Validate before calling

if (!diffManager.hasBeforeFlow()) return // no snapshot: nothing to reject

Try / catch

try { rejectModule(id) } catch (e) { if ((e as Error).message.includes('beforeFlow')) reopenDiffReview(); else throw e }

Prevention

When it happens

Trigger: Calling rejectModule(id, flowStore?) — directly or via rejectAll — on a flowDiffManager instance created without a beforeFlow snapshot (e.g. manager instantiated outside a diff-review session, snapshot cleared after accept-all/reset, or reject invoked after the review session ended).

Common situations: UI still showing the diff panel after the snapshot was consumed/reset; calling rejectModule on a manager instance that was never initialized with the original flow; race where the review session is torn down while a reject request is in flight.

Related errors


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