windmill-labs/windmill · error

Can only fork a script module

Error message

Can only fork a script module

What it means

fork converts a flow module into an inline editable script: it must fetch the script referenced by flowModule.value.path via createInlineScriptModuleFromPath. Forking only makes sense for modules whose value.type is 'script'; for hub/flow/while/branch/etc. modules there is no single script path to inline, so fork throws 'Can only fork a script module'.

Source

Thrown at frontend/src/lib/components/flows/flowStateUtils.svelte.ts:239

			type: 'flow',
			path: '',
			input_transforms: {}
		},
		summary: ''
	}

	const flowModuleState = await loadFlowModuleState(flowFlowModules)

	return [flowFlowModules, flowModuleState]
}

export async function fork(
	flowModule: FlowModule,
	// The acting workspace when the flow editor runs in an AI session; else the nav workspace.
	workspace?: string
): Promise<[FlowModule & { value: RawScript }, FlowModuleState]> {
	if (flowModule.value.type !== 'script') {
		throw new Error('Can only fork a script module')
	}
	const forkedFlowModule = await createInlineScriptModuleFromPath(
		flowModule.value.path ?? '',
		flowModule.id,
		workspace
	)
	const flowModuleState = await loadFlowModuleState(forkedFlowModule)
	return [forkedFlowModule, flowModuleState]
}

async function createInlineScriptModuleFromPath(
	path: string,
	id: string,
	workspace?: string
): Promise<FlowModule & { value: RawScript }> {
	const { content, language } = await getScriptByPath(path, workspace)

	return {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check flowModule.value.type === 'script' before calling fork and hide/disable the fork action for other types.
  2. If forking whole flows, filter modules to script-type steps and skip/handle the rest explicitly.
  3. For non-script modules, use the appropriate conversion path (e.g. re-creating the step) instead of fork.

Example fix

// before
const [mod, state] = await fork(flowModule)
// after
if (flowModule.value.type !== 'script') return
const [mod, state] = await fork(flowModule)
Defensive patterns

Strategy: type-guard

Validate before calling

if (flowModule.value.type !== 'script') return

Type guard

function isScriptModule(m: FlowModule): m is FlowModule & { value: { type: 'script'; path?: string } } { return m.value.type === 'script' }

Try / catch

try { await fork(fm) } catch (e) { if ((e as Error).message === 'Can only fork a script module') toast('Only script steps can be forked'); else throw e }

Prevention

When it happens

Trigger: Calling fork(flowModule, workspace?) with a FlowModule whose value.type is not 'script' (e.g. a nested flow, hub item, loop, or raw-app step), typically from an 'inline/fork this step' UI action or custom tooling iterating over all modules.

Common situations: Batch-forking every module of a flow without filtering by type; attempting to inline a 'flow' or 'hub' step the UI shouldn't offer the action for; stale type info after a module was converted between kinds.

Related errors


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