Environment failures: a troubleshooting manual
September 2026. Five "the code did not change, the environment did" failures in a single day. This note records them in one shared format and turns the result into a checklist that can be followed directly.
Short version
- The common trait of environment failures is that the symptom is not where the cause is: the error is in the command, the cause is in the PATH;
- The counter-intuitive one: most of these need no sudo. Using privilege to silence a permission error usually converts a visible problem into an invisible one;
- The first move is always the same: establish which thing you actually called (
which -a/type -a). A good share of "the command does not work" ends right there; - The real loss stopper is not fixing it but recording it in one consistent shape, so next time is a lookup.
1. Why: five failures in one day
That day I had to run training scripts, generate charts with Chinese labels, and package the output in a fresh environment. Once dependencies were in place the failures arrived in sequence: the system Python had been replaced, WSL could not resolve names, global package installation hit permission errors, a command rewrote paths into Windows style, and a cleanup script killed itself. They had nothing to do with each other, but the diagnosis followed the same shape — worth recording with one template.
2. One shared structure
Each case is written in five parts: symptom → diagnostic action → root cause → fix → how to avoid it, and only the fifth decides how much time the next occurrence costs. Quick reference:
| Symptom | Diagnostic | Root cause | Fix |
|---|---|---|---|
| System Python tools report missing modules | which -a python3 order | Init script puts the base environment first in PATH | Disable auto-activation, use isolated environments |
| WSL cannot resolve names, installs stall | cat /etc/resolv.conf vs host | Resolver config generated by host, not resynced | Disable generation, set the resolver explicitly |
| Global installs fail with permission errors | npm config get prefix | Prefix points at a system directory | Move the prefix to the user directory or use a version manager |
| Arguments rewritten into Windows paths | echo the same argument | Compatibility layer converts Unix-style paths | Disable path conversion for that call |
| A cleanup command kills itself | pgrep -af first | Pattern matches the full command line, including its own | Break self-matching with a character class, or resolve PIDs first |
| A script is blocked by policy or a hook | Read the source named in the error | Policy restricts scripts from unknown sources | Adjust the policy scope, or follow the hook's error |
| Chinese chart output is all boxes | fc-list :lang=zh empty | No CJK font installed | Install the font and set the family explicitly |
3. Three cases in detail
Case one: the package manager moved the system Python
Symptom: system Python tools started reporting ModuleNotFoundError, and the script that installs system packages failed outright.
Diagnostic: in which -a python3, the interpreter shipped by the package manager came before /usr/bin/python3 — two Pythons that cannot see each other's packages.
Root cause: auto-initialisation was confirmed at install time. The init script was appended to the shell config, putting the base environment at the front of PATH on every new terminal and activating it.
Fix: disable base auto-activation, isolate dependencies per project, and use absolute paths wherever a script calls the system interpreter.
Avoid: put "never install project dependencies into base" in the rules file, and replace bare python in scripts with an explicit path.
Case two: the compatibility layer rewrote my argument
Symptom: a command that works elsewhere reported "path not found" here, with a C:/Program Files/Git/ prefix I had never typed.
Diagnostic: the same argument echoed on its own is unchanged; it is rewritten only once it crosses into an external program — so the rewriting happens at the call boundary, not in my input.
Root cause: the compatibility layer converts arguments shaped like /xxx into Windows paths so that Unix-style paths reach Windows programs.
Fix: disable path conversion for that particular call; to allow a single argument through, use the exclusion variable rather than switching it off globally.
Avoid: before calling an external program, check any argument starting with / for rewriting.
Case three: the cleanup script killed itself
Symptom: after the name-matching cleanup command ran, the target process was gone — but the finishing logic after that command never executed.
Diagnostic: the pgrep -af hit list included the shell that ran the command, whose own command line contained the same keyword.
Root cause: pattern matching inspects full command lines, and the process initiating the match carries the pattern in its own.
Fix: write one character of the pattern as a character class so it no longer matches itself, or resolve PIDs first, drop your own, and kill the rest.
Avoid: list the hits before acting on any pattern-based process operation. "Look before you kill" needs no reasoning attached.
Worth noting
The most expensive way to handle these is privilege: sudo for permission errors, wholesale path rewriting for "file not found". Both make the current command pass and cause the problem to reappear elsewhere in a harder-to-find form. Ask why it is this one before asking how to get past it.
4. The font class: no error, unusable output
The cases above all exit non-zero. A missing font does not: the program finishes normally, leaves one warning in the log, and every Chinese label in the output becomes a box. "Succeeds but unusable" is the most dangerous class, because automated checks look at exit codes and pass everything. The cheapest response is to make visual inspection a pre-delivery step: generate the artefact and actually look at it, especially charts and documents containing Chinese.
Best value for effort
Every time an environment problem is fixed, append one "symptom → one-line fix" entry to the rules file. These problems recur, and one line of writing replaces one full investigation — the only compounding action in this note.
5. Reusable checklist
- Establish the entity first:
which -a/type -a, comparing version and install location; - Check PATH order, especially injected directories ahead of system ones;
- When an error mentions a path you never wrote, suspect boundary rewriting first;
- For permission errors, change the install location or prefix before reaching for privilege;
- List the hit list before running any pattern-based process operation;
- Record every case as symptom → diagnostic → root cause → fix → avoidance; no fifth part means it is not fixed;
- Separate "the command failed" from "it succeeded but the output is unusable"; only the second needs human eyes.