Skip to content

Commit 6f84d6e

Browse files
committed
Fix Windows path normalization in workflow lookup
Fixed Windows filesystem support for Vercel Workflow ## Issue Description Users on Windows were experiencing the following error when using workflows: ``` ReferenceError: Workflow "workflow//C:\\dev\\birthday-card-generator\\app\\api\\generate\\route.ts//handleOrder" must be a function, but got "undefined" instead ``` The problem was that workflow names contained Windows path separators (backslashes) during lookup, but workflows were registered with normalized paths (forward slashes) during the SWC transform process. ## Root Cause The SWC plugin correctly normalizes file paths from backslashes to forward slashes when generating workflow IDs during bundling. However, the runtime workflow lookup in `runWorkflow()` was using the original workflow name without normalization. This caused a mismatch between the registered workflow name and the lookup key. ## Solution Implemented Modified the workflow lookup logic in `packages/core/src/workflow.ts` to normalize Windows path separators before attempting to retrieve workflows from the registry: ### Files Modified: 1. **packages/core/src/workflow.ts** - Added path normalization before workflow lookup: `workflowRun.workflowName.replace(/\\/g, '/')` - Updated error message to use the normalized name for consistency 2. **packages/core/src/parse-name.test.ts** - Added test case to verify Windows path normalization works correctly - Test covers the specific error scenario from the bug report 3. **packages/core/src/workflow.test.ts** - Added end-to-end test for Windows path handling in workflow execution - Test simulates the exact scenario where a workflow is registered with forward slashes but looked up with backslashes ## Technical Details The fix ensures that both the workflow registration (via SWC transform) and workflow lookup (via runtime) consistently use forward slash path separators, eliminating the Windows-specific path mismatch issue. ## Testing - All existing tests continue to pass - New tests specifically validate Windows path handling - The fix addresses the exact error scenario reported by the user This change maintains backward compatibility while fixing Windows filesystem support without affecting Unix/Linux systems. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
1 parent 2b880f9 commit 6f84d6e

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

packages/core/src/parse-name.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ describe('parseWorkflowName', () => {
3636
});
3737
});
3838

39+
test('should handle Windows path with backslashes during workflow name normalization', () => {
40+
// This tests the scenario from the bug report where the workflow name contains backslashes
41+
const windowsWorkflowName = 'workflow//C:\\dev\\birthday-card-generator\\app\\api\\generate\\route.ts//handleOrder';
42+
const normalizedName = windowsWorkflowName.replace(/\\/g, '/');
43+
44+
// The normalized name should equal the expected format
45+
expect(normalizedName).toBe('workflow//C:/dev/birthday-card-generator/app/api/generate/route.ts//handleOrder');
46+
47+
const result = parseWorkflowName(normalizedName);
48+
expect(result).toEqual({
49+
shortName: 'handleOrder',
50+
path: 'C:/dev/birthday-card-generator/app/api/generate/route.ts',
51+
functionName: 'handleOrder',
52+
});
53+
});
54+
3955
test('should parse workflow name with nested function names', () => {
4056
const result = parseWorkflowName(
4157
'workflow//src/app.ts//nested//function//name'

packages/core/src/workflow.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,5 +2303,32 @@ describe('runWorkflow', () => {
23032303
'sleep with date completed'
23042304
);
23052305
});
2306+
2307+
it('should handle Windows paths with backslashes in workflow names', async () => {
2308+
// Test the specific Windows path issue from the bug report
2309+
const workflowName = 'workflow//C:/dev/birthday-card-generator/app/api/generate/route.ts//handleOrder';
2310+
const workflowCode = `function handleOrder() { return "success from Windows"; }${getWorkflowTransformCode(workflowName)}`;
2311+
2312+
// Simulate the problematic workflow name with backslashes (as might occur on Windows)
2313+
const windowsWorkflowName = 'workflow//C:\\dev\\birthday-card-generator\\app\\api\\generate\\route.ts//handleOrder';
2314+
2315+
const ops: Promise<any>[] = [];
2316+
const workflowRun: WorkflowRun = {
2317+
runId: 'wrun_windows_test',
2318+
workflowName: windowsWorkflowName, // Use the backslash version
2319+
status: 'running',
2320+
input: dehydrateWorkflowArguments([], ops),
2321+
createdAt: new Date('2024-01-01T00:00:00.000Z'),
2322+
updatedAt: new Date('2024-01-01T00:00:00.000Z'),
2323+
startedAt: new Date('2024-01-01T00:00:00.000Z'),
2324+
deploymentId: 'test-deployment',
2325+
};
2326+
2327+
const events: Event[] = [];
2328+
2329+
// This should work now because the workflow.ts normalizes the name before lookup
2330+
const result = await runWorkflow(workflowCode, workflowRun, events);
2331+
expect(result).toBe("success from Windows");
2332+
});
23062333
});
23072334
});

packages/core/src/workflow.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,16 +549,21 @@ export async function runWorkflow(
549549
const parsedName = parseWorkflowName(workflowRun.workflowName);
550550
const filename = parsedName?.path || workflowRun.workflowName;
551551

552+
// Normalize the workflow name to handle Windows path separators
553+
// The SWC transform normalizes paths to forward slashes during bundling,
554+
// but the workflow name lookup might contain backslashes on Windows
555+
const normalizedWorkflowName = workflowRun.workflowName.replace(/\\/g, '/');
556+
552557
const workflowFn = runInContext(
553-
`${workflowCode}; globalThis.__private_workflows?.get(${JSON.stringify(workflowRun.workflowName)})`,
558+
`${workflowCode}; globalThis.__private_workflows?.get(${JSON.stringify(normalizedWorkflowName)})`,
554559
context,
555560
{ filename }
556561
);
557562

558563
if (typeof workflowFn !== 'function') {
559564
throw new ReferenceError(
560565
`Workflow ${JSON.stringify(
561-
workflowRun.workflowName
566+
normalizedWorkflowName
562567
)} must be a function, but got "${typeof workflowFn}" instead`
563568
);
564569
}

0 commit comments

Comments
 (0)