The bug was not in the API client.
It was in the assumption.
A command checked whether `~/secrets/<project>.env` existed. It did. That looked reassuring. Then another command checked `os.getenv('ENV_TOKEN')` inside a Python subprocess, and it came back `False`.
That difference is the lesson.
A secrets file existing on disk does not mean the current process has those values loaded.
That sounds obvious when stated plainly, but it is an easy mistake to make in real automation work. You see the file, you know the project uses a loader like `direnv`, and your brain compresses those facts into one conclusion: the environment is ready.
But readiness lives in the process, not in the file system.
This matters even more when you introduce agents, subprocesses, scripts, or local tooling. Interactive shells often hide the distinction because the loading step has already happened by the time you start typing. In that context, the environment feels ambient. It feels like part of the machine.
It is not.
It is part of that shell session.
The moment you move into non-interactive execution, you have to think more carefully about the contract. Was the environment loader actually active in this shell? Did the subprocess inherit the variables? Is the tool starting from a clean process where nothing has been exported yet?
Those questions are operational, not theoretical. In this session, they changed the interpretation of the evidence.
Checking the file path only proved one thing: a source file exists.
Checking `os.getenv('ENV_TOKEN')` proved the thing that actually mattered: the process was unauthenticated.
That distinction changed the fix.
The useful fix was not just “remember where the secrets file lives.” That is still too vague. The useful fix was making the runtime prerequisite explicit in the workflow:
Run the command from a shell where the project environment has already been loaded. Then verify the required variable from the same runtime boundary before attempting the update.
For example:
`python3 -c "import os; print(bool(os.getenv('ENV_TOKEN')))"`
That single check is more valuable than a lot of hand-wavy reassurance. It tests the real dependency boundary.
This pattern generalizes beyond any one API, repo, or agent workflow. Any process that relies on `.envrc`, shell hooks, local secret files, profile scripts, or secret-loading wrappers has the same failure mode.
Teams often document where secrets live, but not how those secrets become available to the exact process running the command.
That gap is where “it should work” bugs come from.
So the practical rule is simple:
Do not treat environment files as proof of runtime state.
Treat the current process environment as the source of truth.
If a command needs auth, verify auth in that process. If a tool depends on a shell loader, say so explicitly in the project instructions. If the same pattern appears across multiple repos, capture it in the shared agent or operator guidance too.
The deeper lesson is that good automation depends on small, explicit contracts.
Not “the file is there.”
Not “my terminal usually has it.”
Not “this works on my machine.”
The contract is: this process has the variables it needs, right now.
That is the check worth trusting.
-----------
If you find this content useful, please share it with this link: [https://patrickmichael.co.za/subscribe](https://patrickmichael.co.za/subscribe)