There are bugs that make you question your code, and then there are bugs that make you question your understanding of how a language works.
This was the latter.
Recently, while wiriting a script in a project I have been on for quite a while now, I encountered an issue that opened my eyes to understanding the concept of Current Working Directory. Interestingly, I only got to know about this concept because of the snag I experienced.
Let’s dive in!
To put things in context, the “aha” moment came while working on the Single Flight Pattern (SFP) implementation which I talked about in this writeup — you can read it up here (insert link here).
I needed to test the SFP implementation in isolation from the rest of the project, so I wrote the test script, and attempted running it from the command line:
node test-SFP.js
Instead of seeing my test pass, I got the error in the command line shown in the screenshot below (emphasis on the lines with a yellow pointer/arrow);
At first, I assumed I had broken something in the main SFP code I had written.
I hadn’t.
My First Assumption
Since my OAuth client relied on environment variables, I naturally suspected that the variable was not spelt correctly, so I deliberately deleted and rewrote that and retried running the script again, but got same result. I even went ahead to log other . env variables to be sure I could isolate the issue, but each time, I got an undefined printed in the console.
I also at some point begin to change the order of imports in my code to be sure I was not improperly importing related files.
At that point, I even started wondering whether I had misunderstood how ES Modules loaded dependencies.
Looking back, I was making a classic debugging mistake.
I was changing code before I had enough information about the problem.
Time to Stop Guessing
Instead of continuing to make changes, I decided to gather evidence.
The first thing I did was to log dotenv.config() (note that I imported this module into the test file because I was testing the SFP part of my application in isolation).
import dotenv from "dotenv";
const result = dotenv.config();
console.log(result);
The output immediately changed the direction of my investigation.
ENOENT: no such file or directory
See full output of the error / output in the screenshot attached below:
The output in the screenshot was an important discovery for me!
It said there was no .env , yet I was sure it existed — in the project root!
So why was .dotenv insisting that it didn't?
The Clue Was Hidden in the Error Message
Reading the error more carefully, I noticed something I had completely overlooked.
The error wasn’t simply saying that .env couldn't be found.
It was telling me where it was looking.
C:\..\..\bot\src\services\.env
My actual .env file wasn't there.
It lived here instead.
C:\..\..\bot\.env
That completely changed the problem.
The issue was no longer:
“ dotenv can't find my .env file."
The issue became:
“Why is it looking inside src/services in the first place?"
That question eventually led me to the real lesson.
The Discovery
This debugging session introduced me to a Node.js concept that, despite using Node for quite some time, I had never really stopped to think about.
The Current Working Directory (CWD).
Up until this point, I had assumed that because I was executing test-SFP.js, any library — in this case dotenv that needed to find a file would automatically look relative to test-SFP.js.
That wasn’t the case.
When I called:
import dotenv from "dotenv";
dotenv.config();
I wasn’t telling dotenv where my .env file lived.
So dotenv had to make a choice.
By default, it asked Node for the application’s Current Working Directory using:
process.cwd()
and then looked for a .env file there.
This made me curious, so I printed the Current Working Directory.
console.log(process.cwd());
When I executed my test like this:
cd src/services
node test-SFP.js
the output was:
.../bot/src/services
That meant dotenv naturally searched for:
bot/
└── src/
└── services/
└── .env
But my actual project looked like this:
bot/
│
├── .env
├── package.json
└── src/
└── services/
└── test-SFP.js
└── FlutterwaveClient.js
In other words, dotenv wasn't failing to read my *.env * file.
It was simply looking in a different location than I expected.
Once that clicked, everything else started making sense.
Two Ways to Fix It
Once I understood the problem, I realized there were two perfectly valid solutions.
Option 1: Run the application from the project root
Instead of running the test script from src/services like I had been doing, I naviagted to the project root - bot and executed:
cd bot
node src/services/test-SFP.js
Now the Current Working Directory became:
bot/
which is exactly where my .env file lived.
Without changing a single line of code, dotenv.config() worked exactly as intended.
Option 2: Tell dotenv exactly where the file is
Alternatively, dotenv allows you to override its default behavior by specifying the location of the .env file explicitly.
import dotenv from "dotenv";
dotenv.config({
path: "../../.env"
});
When I tried this, I ran my script from inside src/services.
cd src/services
node test-SFP.js
The Current Working Directory remained:
bot/src/services
But this time, dotenv ignored its default lookup location because I had explicitly told it where to find the configuration file.
Both approaches solved the problem.
The important lesson was understanding why the default behavior had failed in the first place.
Once I understood that dotenv.config() uses the application's Current Working Directory by default, both solutions felt perfectly logical instead of feeling like random fixes.
Why This Makes Sense
After understanding the behavior, I actually found it quite reasonable.
Project-wide configuration files such as:
- dotenv
- package.json
- tsconfig.json etc typically live in the project’s root directory.
It makes sense for libraries like dotenv and the others mentioned to assume that the application starts from the project root.
That way, every file in the project shares the same configuration regardless of where the file itself lives.
Lessons For The Future
This experience left me with a few lessons that probably will remember for a long time.
1. A script’s location is not the same as the application’s Current Working Directory.
Those are two different concepts, and they affect how many libraries resolve files.
2. Error messages often tell you more than you initially realize.
The error wasn’t just saying that .env couldn't be found.
It was telling me exactly where it had looked.
I simply hadn’t paid enough attention to that detail.
3. Stop guessing. Start gathering evidence.
The turning point wasn’t another code change.
It was inspecting the output of dotenv.config() and allowing the error message to guide the investigation.
4. Understanding the runtime is just as important as writing code.
This wasn’t really a *dotenv * problem.
It was a Node.js concept that I hadn’t fully understood yet.



Top comments (0)