iTranslated by AI
Fixing the Issue Where VSCode Jest Extension Cannot Find Node.js Managed by asdf
I encountered an issue where the VSCode Jest extension (vscode-jest) sometimes fails to call Node.js managed by asdf. In this post, I will share the results of a deep dive into the cause and its solution.
TL;DR
- The VSCode Jest extension calls Node.js from a non-interactive and non-login
/bin/shshell. - Since asdf initialization code (such as setting up the PATH) is often written in
~/.bashrcor~/.zshrc, the Jest extension cannot call Node.js managed by asdf. - There are several workarounds, but setting the Jest execution shell to a login shell via
jest.shellin.vscode/settings.jsonis probably the easiest. Probably..vscode/settings.json"jest.shell": { "path": "/usr/bin/env", "args": ["zsh", "--login"] },
About the VSCode Jest extension: vscode-jest
VSCode has a useful extension for running Jest[1] tests.
It is called vscode-jest, and it allows you to run Jest tests and view test results directly within VSCode.
What happened
One day, when I opened a project using Node.js in VSCode, vscode-jest threw an error.

> Test run exited at 2023/6/7 18:31:05 <
env: node: No such file or directory
[error] failed to retrieve test file list. TestExplorer might show incomplete test items
[info] jest process failed to start, most likely due to env or project configuration issues, please see: https://github.com/jest-community/vscode-jest/blob/master/README.md#jest-failed-to-run
env: node: No such file or directory
[error] Jest process "watch-tests" ended unexpectedly
[info] jest process failed to start, most likely due to env or project configuration issues, please see: https://github.com/jest-community/vscode-jest/blob/master/README.md#jest-failed-to-run
Two errors appeared: one stating that the test file list could not be retrieved, and another indicating that watch-tests (which monitors files and runs tests automatically) failed. Both errors included env: node: No such file or directory, suggesting they share the same cause.
What was strange was that I had been able to run tests with the Jest extension without any special configuration until then. At first, I couldn't figure out what was different from usual.
Conditions under which it occurs
After trying several things, I realized that the error occurred when I was not using the code command.
Starting VSCode without using the code command means, for example, opening it from the list of recently opened directories by right-clicking VSCode in the Dock, or from the list of recently opened directories in the menu bar.

Opening from the Dock

Opening from the menu bar
In fact, I often open VSCode from the Dock, and I found that's when the error occurs.
The Cause
While searching around, I found the following page written by a volunteer.
The contents of this page and the comments on a vscode-jest issue explain everything, but I'll break it down.
vscode-jest does not read ~/.bashrc and similar files
Apparently, when vscode-jest calls Jest, it calls Node.js—and consequently Jest—from a non-interactive and non-login /bin/sh shell.
Consequently, vscode-jest doesn't read environment variables exported in files like ~/.bashrc.
Where is the path set for a non-login /bin/sh?
To begin with, where is the path set for a non-login /bin/sh?
First, let's check the environment variables with env -i /bin/sh -c 'env'.
❯ env -i /bin/sh -c 'env'
PWD=<path of execution directory>
SHLVL=1
_=/usr/bin/env
Oh, $PATH is missing. Is it defined at all?
We can check with env -i /bin/sh -c 'echo $PATH'.
❯ env -i /bin/sh -c 'echo $PATH'
/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
Even though PATH wasn't defined in env, the contents of $PATH were displayed. This is because PATH is registered as a default shell variable[2].
This is mentioned in man bash.
PATH The search path for commands. It is a colon-separated list of directories in which the shell looks for commands (see COMMAND EXECUTION below). A zero-length (null) directory name in the value
of PATH indicates the current directory. A null directory name may appear as two adjacent colons, or as an initial or trailing colon. The default path is system-dependent, and is set by the
administrator who installs bash. A common value is ``/usr/gnu/bin:/usr/local/bin:/usr/ucb:/bin:/usr/bin''.
The default path is system-dependent, and is set by the administrator who installs bash. A common value is ``/usr/gnu/bin:/usr/local/bin:/usr/ucb:/bin:/usr/bin''.
Apparently, it is system-dependent (decided by the person who installed it). In the case of macOS (darwin?), it might be set by their developers.
At least in the /bin/sh of my environment, the following locations were specified:
/usr/gnu/bin/usr/local/bin/bin/usr/bin.
Therefore, the Node.js binary (node) must exist in one of these locations.
In my environment, the path to Node.js was not set in a non-login /bin/sh
For example, when installing Node.js with Homebrew, it is eventually placed in /opt/homebrew/bin/.
Usually, /opt/homebrew is not in the path (it's not included in $PATH), so you need to run /opt/homebrew/bin/brew shellenv in ~/.bashrc or similar to set the path to /opt/homebrew/bin/ [3].
In my case, I manage Node.js with asdf[4].
❯ where node
/Users/<username>/.asdf/shims/node
/opt/homebrew/bin/node # Note: Node.js was also installed via Homebrew
Binaries managed by asdf are placed in ~/.asdf/shims, so just like with Homebrew, you need to set the path. Since I configure my path via ~/.zshrc, the path was not set in /bin/sh (because it's set in .zshrc, the path isn't set regardless of whether it's login or non-login).
This was the cause.
Solution
The solution was also described in the page I mentioned earlier.
In the author's case, they bypassed the issue by setting their usual shell (Zsh) in jest.shell and setting the path in .zshenv.
vscode-jest has a setting called jest.shell, where you can set the shell used to call Jest (defaults to /bin/sh if nothing is set).
Additionally, in the case of Zsh, the ~/.zshenv file is read in all cases ((non-)login, (non-)interactive)[6]. Therefore, by setting the path in ~/.zshenv, the path to Node.js will be set when Zsh calls Jest.
However, modifying ~/.zshenv affects things like Zsh shell script execution, so I'd prefer not to rewrite it if possible.
As a way to avoid this, the page also mentioned that a change to allow customizing the shell used for running Jest might be coming. It seemed unreleased at that time, but it appeared to have been released already, so this time I tried running Jest as a login shell using --login.
Actual configuration
"jest.shell": {
"path": "/usr/bin/env",
"args": ["zsh", "--login"]
},
So, the actual configuration is as shown above.
In my case, I call Zsh via env and use the --login option to start it as a login shell.
By starting as a login shell, ~/.zprofile and ~/.zlogin are read.
Setting the path to Node.js in those files allows you to avoid modifying ~/.zshenv.
Adding this configuration to the .vscode/settings.json of the target repository resolved the issue mentioned at the beginning.
Note that depending on the situation, this might take slightly longer than starting /bin/sh.
In my case, I didn't notice any particular issues regarding speed.
Conclusion
What started as a mystery with the VSCode Jest extension's behavior led me to find a workaround quickly, but I was curious about the underlying cause and decided to investigate.
I'm glad I could prevent the issue from recurring.
It was a great learning experience regarding the differences in shell modes and the loading order of configuration files.
If I've written anything incorrect or if you have a better solution, please let me know!
-
A JavaScript testing framework. https://jestjs.io ↩︎
-
This behavior likely varies depending on the shell. ↩︎
-
This instruction appears after installing Homebrew. https://github.com/Homebrew/install/blob/716a1d024f32890ef75ea82c18a769abc24e9475/install.sh#L1010-L1025 ↩︎
-
Roughly speaking, it's a version management tool for binaries. You can use different versions of binaries for each directory. ↩︎
-
The mechanism of inheriting environment variables is likely common knowledge, but I looked for a page listing the specifications. I couldn't find one, so if anyone knows, please let me know. ↩︎
-
For details, see
STARTUP/SHUTDOWN FILESinman zsh. https://linux.die.net/man/1/zsh#STARTUP/SHUTDOWN FILES ↩︎
Discussion