Fixing VS Code Multi-Root Workspace Launch Configs That Resolve ${workspaceFolder} to the Wrong Folder
A debugger launch configuration that works perfectly in a single-folder project starts pointing at the wrong path, or fails to find the program at all, the moment that same project becomes one folder inside a multi-root workspace. `${workspaceFolder}` didn't become unreliable — it was always resolving relative to a specific folder, and a multi-root workspace just has more than one candidate for that now.
The Problem
A launch.json configuration that debugs a project correctly — pointing to the right entry file, running from the right working directory — keeps working fine as long as that project is opened as a single folder in VS Code. The moment the same project becomes one of several folders inside a multi-root workspace (a .code-workspace file listing it alongside a backend repo, a shared library, or another service), the exact same launch configuration either points at the wrong file, fails with a "file not found" error, or runs against a completely different folder's code than the one intended.
Why It Happens
${workspaceFolder} resolves to exactly one specific folder, and in a multi-root workspace there's more than one candidate
In a single-folder window, ${workspaceFolder} is unambiguous — there's only one folder open, so it always resolves to that one. In a multi-root workspace, VS Code has to pick which of the several open folders that variable actually refers to, and by default it resolves to whichever folder currently has focus in the editor (based on the active file) rather than a folder deliberately tied to that specific launch configuration. A configuration that was written and tested in a single-folder context, where this ambiguity never existed, can carry that assumption forward unnoticed into the multi-root setup.
launch.json and tasks.json defined at the workspace level apply across all folders, with no built-in folder-scoping unless it's explicitly added
A .code-workspace file can define its own top-level launch and tasks sections that apply across the whole workspace, separate from any launch.json sitting inside an individual folder. A configuration placed in that workspace-level section has no inherent tie to any one folder — it's available regardless of which folder's file happens to be focused — and relies entirely on either an explicit folder-scoped variable or the user's current focus to resolve correctly, which is exactly the ambiguity that produces the wrong-path symptom.
The more specific ${workspaceFolder:name} syntax exists specifically for this case, but defaults don't use it automatically
VS Code supports a folder-qualified form, ${workspaceFolder:folderName}, that resolves unambiguously to a specific named folder within the multi-root workspace regardless of what's currently focused. A configuration copied forward from a single-folder setup, or generated by a framework's default debug-configuration scaffolding, typically uses the plain, unqualified ${workspaceFolder} because that's all a single-folder context ever needed, and nothing automatically upgrades it to the qualified form when the project later becomes part of a multi-root workspace.
The Fix
1. Qualify the variable with the specific folder name wherever a launch configuration needs to target one folder unambiguously
{
"name": "Debug API Server",
"type": "node",
"request": "launch",
"program": "${workspaceFolder:api-server}/src/index.js",
"cwd": "${workspaceFolder:api-server}"
}
Naming the folder explicitly removes the dependency on which file currently has focus entirely — the configuration resolves to the same folder every time it's run, regardless of what else is open in the workspace, which is the direct fix for the ambiguity that causes the wrong-path symptom.
2. Check whether the workspace file name actually matches the folder's name as VS Code displays it, since they can silently differ
// In the .code-workspace file:
{
"folders": [
{ "name": "api-server", "path": "./backend" },
{ "name": "web-app", "path": "./frontend" }
]
}
The folder-qualified variable refers to the folder's display name as defined in the workspace file's folders array (or the directory's own basename if no explicit name is set), not necessarily the literal directory name on disk — confirming the actual name being resolved against avoids a qualified reference that looks correct but targets the wrong entry because of a name mismatch.
3. Move a configuration meant for exactly one folder into that folder's own local launch.json, rather than the shared workspace-level one
Place the configuration in: backend/.vscode/launch.json
rather than the top-level launch section of the .code-workspace file.
A configuration defined inside a specific folder's own .vscode/launch.json is inherently scoped to that folder — ${workspaceFolder} used there resolves to that folder by construction, with no qualification needed — which sidesteps the ambiguity entirely for any configuration that was only ever meant to apply to one specific folder in the first place.
4. Test the configuration specifically with a different folder's file focused, not just the intended target folder's file
Open a file in a different folder of the workspace, then run the launch
configuration, to confirm it still resolves to the intended folder rather
than wherever the editor's focus happens to be.
Because the unqualified variable's default behavior depends on which folder currently has focus, testing a launch configuration only while the intended folder's file is open can pass even when the configuration is still ambiguous — deliberately testing with a different folder focused is what actually surfaces the bug before it reaches a teammate's machine in a different focus state.
Why This Works
Each fix removes a different source of the same underlying ambiguity. Qualifying the variable with an explicit folder name is the direct fix, making the resolution unambiguous regardless of focus. Confirming the actual folder name catches a qualified reference that's silently wrong due to a naming mismatch rather than the ambiguity itself. Moving a single-folder configuration into that folder's own local launch.json avoids the ambiguity by construction, since there's only one folder for the variable to mean in that scope. And testing with a different folder focused is what actually reveals the dependency on focus, rather than a test that happens to pass because the right folder was already focused by coincidence.
Conclusion
A launch configuration that points at the wrong folder only after a project joins a multi-root workspace isn't a VS Code bug — it's ${workspaceFolder} correctly doing what it was always designed to do, resolve to whichever folder has focus, in a context that now has more than one folder to choose from. Qualify the variable with the specific folder name wherever a configuration needs to target one folder unambiguously, confirm that name matches what the workspace file actually defines, move single-folder configurations into that folder's own local launch.json when possible, and test with a different folder focused to catch the dependency before it reaches someone else's machine.
