windmill-labs/windmill · error · Error
taskScript("${path}") can only be called inside a workflow()
Error message
taskScript("${path}") can only be called inside a workflow() What it means
taskScript(path) is a step-composition helper that must run under an active workflow() context: it delegates to ctx._nextStep to append a script step to the workflow being built. When no workflow context is registered, the wrapper throws this error naming the path (typescript-client/client.ts:2267), because a script task outside workflow() has nothing to be scheduled onto.
Source
Thrown at typescript-client/client.ts:2267
/**
* Create a task that dispatches to a separate Windmill script.
*
* @example
* const extract = taskScript("f/data/extract");
* // inside workflow: await extract({ url: "https://..." })
*/
export function taskScript(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any> {
const name = path.split("/").pop() || path;
const wrapper = function (...args: any[]) {
const ctx: WorkflowCtx | null = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
if (ctx) {
const kwargs = args.length === 1 && typeof args[0] === "object" && args[0] !== null
? args[0]
: args.reduce((acc, v, i) => { acc[`arg${i}`] = v; return acc; }, {} as Record<string, any>);
return ctx._nextStep(name, path, kwargs, "script", options);
}
throw new Error(`taskScript("${path}") can only be called inside a workflow()`);
};
Object.defineProperty(wrapper, "name", { value: name });
(wrapper as any)._is_task = true;
(wrapper as any)._task_path = path;
return wrapper;
}
/**
* Create a task that dispatches to a separate Windmill flow.
*
* @example
* const pipeline = taskFlow("f/etl/pipeline");
* // inside workflow: await pipeline({ input: data })
*/
export function taskFlow(path: string, options?: TaskOptions): (...args: any[]) => PromiseLike<any> {
const name = path.split("/").pop() || path;
const wrapper = function (...args: any[]) {
const ctx: WorkflowCtx | null = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");View on GitHub (pinned to e474e8803c)
Solutions
- Move the taskScript call inside the workflow() callback so the context is active.
- If calling from a helper, keep the helper synchronous within the workflow callback scope.
- For tests, use a harness/stub that installs a workflow context (setWorkflowCtx).
- Check that an earlier await did not detach the call from the active workflow context.
Example fix
// before
export async function main() {
await greet('world'); // taskScript outside workflow() -> throws
}
// after
export async function main() {
await workflow(async () => {
await greet('world'); // inside workflow context
});
} Defensive patterns
Strategy: try-catch
Validate before calling
const inWorkflow = () => Boolean(_workflowCtx ?? Reflect.get(globalThis, '__wmill_wf_ctx'));
Type guard
function requireWorkflowCtx(): WorkflowCtx {
const ctx: WorkflowCtx | null = _workflowCtx ?? Reflect.get(globalThis, '__wmill_wf_ctx');
if (!ctx) throw new Error('taskScript called outside workflow()');
return ctx;
} Try / catch
try {
await greet('world');
} catch (e) {
if (e.message.includes('can only be called inside a workflow()')) {
// fall back to direct execution for standalone runs
await windmill.runScript('u/greet', { args: ['world'] });
} else throw e;
} Prevention
- Always invoke task functions inside the workflow() callback.
- Use a workflow test harness/stub context for unit tests.
- Avoid fire-and-forget or detached awaits around task calls.
- Keep one bundled copy of the client so the context global matches.
When it happens
Trigger: Calling a taskScript-created function at module top level, inside a plain main(), or after the workflow() callback returned; context not propagated into async callbacks or nested closures.
Common situations: Calling the task in unit tests without a workflow harness; awaiting the task outside the workflow callback; refactoring moved the call out of workflow() scope; the global ctx unset because multiple client bundles were loaded.
Related errors
- taskFlow("${path}") can only be called inside a workflow()
- waitForApproval can only be called inside a workflow()
- No workflow() entrypoint found. Wrap your main function with
- You can't use this function in a standalone script or flow s
- WAC step key "${options.key}" is already used in this workfl
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/8ec970bae5e29b0d.
Report an issue: GitHub.