{"record":{"id":"d61a03e778409ec5","repo":"windmill-labs/windmill","slug":"can-only-fork-a-script-module","errorCode":null,"errorMessage":"Can only fork a script module","messagePattern":"Can only fork a script module","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/flows/flowStateUtils.svelte.ts","lineNumber":239,"sourceCode":"\t\t\ttype: 'flow',\n\t\t\tpath: '',\n\t\t\tinput_transforms: {}\n\t\t},\n\t\tsummary: ''\n\t}\n\n\tconst flowModuleState = await loadFlowModuleState(flowFlowModules)\n\n\treturn [flowFlowModules, flowModuleState]\n}\n\nexport async function fork(\n\tflowModule: FlowModule,\n\t// The acting workspace when the flow editor runs in an AI session; else the nav workspace.\n\tworkspace?: string\n): Promise<[FlowModule & { value: RawScript }, FlowModuleState]> {\n\tif (flowModule.value.type !== 'script') {\n\t\tthrow new Error('Can only fork a script module')\n\t}\n\tconst forkedFlowModule = await createInlineScriptModuleFromPath(\n\t\tflowModule.value.path ?? '',\n\t\tflowModule.id,\n\t\tworkspace\n\t)\n\tconst flowModuleState = await loadFlowModuleState(forkedFlowModule)\n\treturn [forkedFlowModule, flowModuleState]\n}\n\nasync function createInlineScriptModuleFromPath(\n\tpath: string,\n\tid: string,\n\tworkspace?: string\n): Promise<FlowModule & { value: RawScript }> {\n\tconst { content, language } = await getScriptByPath(path, workspace)\n\n\treturn {","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/flows/flowStateUtils.svelte.ts#L221-L257","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check flowModule.value.type === 'script' before calling fork and hide/disable the fork action for other types.","If forking whole flows, filter modules to script-type steps and skip/handle the rest explicitly.","For non-script modules, use the appropriate conversion path (e.g. re-creating the step) instead of fork."],"exampleFix":"// before\nconst [mod, state] = await fork(flowModule)\n// after\nif (flowModule.value.type !== 'script') return\nconst [mod, state] = await fork(flowModule)","handlingStrategy":"type-guard","validationCode":"if (flowModule.value.type !== 'script') return","typeGuard":"function isScriptModule(m: FlowModule): m is FlowModule & { value: { type: 'script'; path?: string } } { return m.value.type === 'script' }","tryCatchPattern":"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 }","preventionTips":["Only render the fork/inline action for script-type modules.","When batch-processing flows, branch on value.type before forking.","Re-check type after any module-conversion operation (script <-> flow)."],"tags":["flow-editor","fork","type-check","frontend"],"backgroundTag":"unsupported-module-type","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}