#6353 Relative imports from nested main in base_dir breaks
Bug confirmed in wrangler 4.60.0. PR #6971 attempted but failed to fix. modulesRoot in miniflare/index.ts incorrectly set to entry dir instead of base_dir, breaking module resolution.
Implement one-line fix: use config.bundle.entry.moduleRoot instead of path.dirname(scriptPath)
Analysis Report
Issue Review: cloudflare/workers-sdk#6353
Summary
Relative imports from a nested main entry point fail when using base_dir with no_bundle = true because the modulesRoot for Miniflare is incorrectly set to the entry point's directory instead of the configured base_dir.
Findings
- Created: 2024-07-28
- Updated: 2025-10-30
- Version: 3.67.1 -> 4.60.0 (current)
- Component: Wrangler (deployment-bundle, dev/miniflare)
- Labels: bug
- Comments: 0
Key Evidence
- PR #6971 explicitly states it "was supposed to fix (but doesn't) #6353"
- Issue #6353 is NOT mentioned in any changelog
- Bug successfully reproduced with wrangler 4.60.0 - exact same error as reported
- Root cause identified in source code
Reproduction Results
Using the exact configuration from the issue with wrangler 4.60.0:
✘ [ERROR] service core:user:issue-6353-repro: Uncaught Error: internal error
✘ [ERROR] The Workers runtime failed to start.
Recommendation
Status: KEEP OPEN
Reasoning: The bug is confirmed to still exist in the latest version. PR #6971 attempted but failed to fix this issue. The root cause has been identified in the source code.
Action: Implement the fix described below.
Root Cause Analysis
The issue is in how Miniflare's modulesRoot is constructed in packages/wrangler/src/dev/miniflare/index.ts:179:
const modulesRoot = path.dirname(scriptPath);
Where scriptPath is the resolved entry point path (e.g., /path/to/some/base_dir/nested/index.js).
The Problem Flow:
Entry Configuration (
entry.ts:~133):moduleRootis correctly set fromconfig.base_dir(e.g.,./some/base_dir)- This is used by
findAdditionalModules()to discover modules
Module Discovery (
find-additional-modules.ts:~74):- Scans
entry.moduleRoot(thebase_dir) - Module
foo.jsis found and added with namefoo.js(relative tobase_dir) - Entry point filter (line ~77):
filter((m) => m.name !== relativeEntryPoint)- usespath.relative(entry.moduleRoot, entry.file)which givesnested/index.js
- Scans
Miniflare Module Setup (
miniflare/index.ts:179-193):const modulesRoot = path.dirname(scriptPath); // = /path/to/some/base_dir/nested/ const sourceOptions: SourceOptions = { modulesRoot, modules: [ { type: ..., path: scriptPath, contents: entrypointSource }, // Full path ...modules.map((module) => ({ type: ..., path: path.resolve(modulesRoot, module.name), // WRONG! contents: module.content, })), ], };The Mismatch:
- Entry point path:
/path/to/some/base_dir/nested/index.js modulesRoot:/path/to/some/base_dir/nested/(dirname of entry point)- Additional module
foo.jsresolved path:/path/to/some/base_dir/nested/foo.js - But it should be:
/path/to/some/base_dir/foo.js
- Entry point path:
At Runtime:
- Import
../foo.jsfromnested/index.jstries to resolve/foo.js(going up one level) - But workerd's virtual filesystem has
foo.jsat/nested/foo.js(wrong location) - Result: "internal error" because the module can't be found
- Import
Proposed Solution
Option 1: Use entry.moduleRoot for modulesRoot (Recommended)
In packages/wrangler/src/dev/miniflare/index.ts, change line 179 from:
const modulesRoot = path.dirname(scriptPath);
To:
const modulesRoot = config.bundle.entry.moduleRoot;
This ensures the modulesRoot matches where additional modules were discovered.
Option 2: Calculate relative entry point path
Alternatively, ensure the entry point's module path is relative to the moduleRoot:
const modulesRoot = config.bundle.entry.moduleRoot;
const relativeEntryPath = path.relative(modulesRoot, scriptPath);
const sourceOptions: SourceOptions = {
modulesRoot,
modules: [
{
type: ModuleTypeToRuleType[config.bundle.type],
path: path.resolve(modulesRoot, relativeEntryPath), // or just use scriptPath directly
contents: entrypointSource,
},
...modules.map((module) => ({
type: ModuleTypeToRuleType[module.type ?? "esm"],
path: path.resolve(modulesRoot, module.name),
contents: module.content,
})),
],
};
Files to Modify
packages/wrangler/src/dev/miniflare/index.ts- Primary fix location- Line ~179: Change
modulesRootto useconfig.bundle.entry.moduleRoot
- Line ~179: Change
Potential secondary locations (may need alignment):
packages/wrangler/src/deployment-bundle/create-worker-upload-form.ts- For deploy path (if same issue exists)
Implementation Difficulty: Easy
Justification:
- Single line change in most cases
- The correct value (
entry.moduleRoot) is already available in theconfig.bundle.entryobject - No architectural changes required
- Existing test case was added in PR #6971, just need to verify it passes
Testing Recommendations
Verify existing test passes: PR #6971 added a relevant test case - ensure it now passes with the fix
Manual reproduction test:
- some/ - base_dir/ - nested/ - index.js (imports ../foo.js) - foo.js - wrangler.toml (main = "./some/base_dir/nested/index.js", base_dir = "./some/base_dir")Additional test cases:
- Deeply nested entry points (e.g.,
base_dir/a/b/c/index.jsimporting../../shared/util.js) - Entry point at base_dir root (should still work)
- Mix of relative imports going up and down directories
- Deeply nested entry points (e.g.,
Deploy path verification: Ensure the fix doesn't break
wrangler deploywith the same configuration
Suggested Comment
This issue is confirmed as still occurring in wrangler 4.60.0. The root cause has been identified:
In
packages/wrangler/src/dev/miniflare/index.ts,modulesRootis set topath.dirname(scriptPath)(the entry point's directory), but it should useconfig.bundle.entry.moduleRoot(the configuredbase_dir). This causes additional modules to be placed in the wrong location in workerd's virtual filesystem, breaking relative imports.The fix is a one-line change. Would a PR be welcome?
Notes & Feedback (0)
No notes yet.