# Debug Env Variables

> Debug Environment Variables

- Skill: `yakeworld/debug-env-variables` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add yakeworld/debug-env-variables`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yakeworld/debug-env-variables/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: yakeworld (https://skillmd.com/u/yakeworld)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/yakeworld/debug-env-variables

---



# Debug Environment Variables

## IO_CONTRACT

- **input**: `missing_var: str` — 在非交互 shell / `subprocess.run(shell=True)` 中丢失的环境变量名
- **input**: `shell_context: enum` — 故障层（interactive / `bash -c` / Python subprocess），用于边界隔离诊断
- **output**: `diagnosis: str` — 变量消失点定位（`.bashrc` 守卫 / 引号与 subprocess 边界 / 从未设置）
- **output**: `fix: config` — 按可靠性排序的修复：export 前移守卫之前 / `BASH_ENV` / `.api_key` 文件回退 / `/etc/environment`

## 原则 (Principles)

- **界生惑**：壳有界而境无边，子不知父之所有；变量之失，多在层界交接之处，先明其界再究其源。
- **逐层而验**：从父进程、`bash -c` 至 Python 子层逐层验证变量所在，定位消失点，不凭臆断。
- **修复有序**：export 前移 → `BASH_ENV` → 文件回退（`.api_key`）→ `/etc/environment`，依可靠性为序，勿倒置。
- **自动化不倚 `.bashrc`**：CI、cron、子进程之环境变量，不倚 `.bashrc` 之 export，须用 `/etc/environment` 或文件回退；秘钥更不置 `.bashrc`。


## Genes (策略基因)

> 紧凑策略表示。条件→策略。需要深度时参考完整文档。

- **[DEBU-001]** 变量在交互 shell 存在但在 `bash -c` 或子进程中丢失 → 优先检查 `.bashrc` 中的 `case $- in *i*)` 守卫，确认 export 是否位于守卫之前
- **[DEBU-002]** 需要在非交互 shell 中加载环境变量 → 设置 `BASH_ENV` 指向配置文件，或将 export 语句移至 `.bashrc` 守卫块之前
- **[DEBU-003]** 自动化场景（CI/cron/subprocess）中环境变量不可靠 → 禁止依赖 `.bashrc`，改用 `/etc/environment` 或应用层文件回退机制
- **[DEBU-004]** 敏感凭证（API Key）在子进程中读取为空 → 实现文件回退策略（如读取 `.api_key` 文件），避免将密钥硬编码在 shell 配置中
- **[DEBU-005]** 嵌套 `subprocess.run(shell=True)` 导致引号解析错误 → 使用 `&&` 链接命令或 heredoc 结构，避免在双引号内嵌套单引号导致变量被外层 shell 消费
- **[DEBU-006]** 诊断变量消失点时缺乏定位依据 → 执行逐层验证（父进程 → `bash -c` → Python 子层），通过 `os.environ` 和 `echo` 对比定位具体失效层级

## 原理层·文言

> 环境之变，变量之惑。
> 壳有界而境无边，子不知父之所有。
> 知其源则知其变，明其界则明其惑。

##
DevOps — environment configuration, subprocess shells, credential injection.

## When to Use
- Environment variables work in interactive shell but not in scripts
- `subprocess.run(shell=True)` seems to lose environment variables
- API keys, tokens, or credentials are empty at runtime despite being set
- `.bashrc`/`.profile`/`.env` files seem to be ignored by automated processes

### The Non-Interactive Shell Trap
Bash's `.bashrc` contains this guard by default:
```bash
case $- in
    *i*) ;;
      *) return;;
esac
```
Non-interactive shells (e.g., `bash -c "..."`, `subprocess.run(shell=True)`) do NOT have the `i` flag, so they hit `return` before reaching any code after the guard. **Exports placed after `esac` are invisible to non-interactive shells.**

### Process Substitution and Quoting
When nesting `bash -c` with Python inside `subprocess.run(shell=True)`:
- Single quotes inside double-quoted `-c` args get consumed by the outer shell
- `bash -c "export X=hello; python3 -c 'print(os...)'` — the inner single quotes are eaten
- Use `&&` chaining or heredocs to avoid quoting issues

### `.bashrc` Is Not Loaded Everywhere
- Non-interactive, non-login shells: **never** source `.bashrc`
- Login shells: source `.profile` → which may source `.bashrc`
- `bash --login`: sources `.profile` (not `.bashrc` directly)
- `bash -i`: sources `.bashrc` (interactive)
- `bash -c`: does NOT source anything unless `BASH_ENV` is set

## Debugging Steps

1. **Isolate the boundary**: Does the variable exist in the parent process?
   ```python
   import os; print(os.environ.get("VAR", "NOTSET"))
   ```

2. **Test at each layer**:
   - Interactive shell: `echo $VAR`
   - Non-interactive shell: `bash -c "echo $VAR"`
   - After sourcing: `bash -c "source ~/.bashrc; echo $VAR"`
   - In Python: `bash -c "python3 -c 'import os; print(os.environ.get(\"VAR\"))'"`

3. **Trace through the chain**: Check where the variable disappears:
   - If it exists interactively but not in `bash -c`: the `.bashrc` guard is the culprit
   - If it exists in `bash -c` but not in Python: quoting/subprocess boundary issue
   - If it never exists: the variable was never set correctly

## Fixes (in order of reliability)

### Fix 1: Move Exports Before the Guard
Place `export VAR=...` **before** the `case $- in ... esac` block in `.bashrc`. Non-interactive shells will hit the export before hitting `return`.

### Fix 2: Set BASH_ENV
```bash
export BASH_ENV=~/.bashrc
```
Non-interactive shells read the file pointed to by `BASH_ENV`.

### Fix 3: File-Based Fallback (Most Reliable for Automated Systems)
Store credentials in a local file and have the application read it as fallback:
```python
# In config.py or equivalent
import pathlib
_key_file = pathlib.Path(__file__).parent / '.api_key'
if _key_file.is_file():
    self.api_key = _key_file.read_text().strip()
```
Place the file in a tracked but gitignored location (e.g., `.api_key` in `.gitignore`).

### Fix 4: Use `/etc/environment` (Root Required)
All PAM-authenticated sessions read `/etc/environment`. This is the most reliable but requires root/sudo.

## Verification Checklist
- [ ] Variable exists in interactive shell (`echo $VAR`)
- [ ] Variable survives `bash -c` (non-interactive)
- [ ] Variable is accessible in Python subprocess
- [ ] The application can read the variable (directly or via fallback)

## Hermes Agent / Python Subprocess Specific Pitfalls

### The `subprocess.run(shell=True)` Trap in Hermes
When Hermes Agent runs `execute_code` with `shell=True`, it invokes `bash -c`. This shell:
- Does **NOT** have the `i` flag (non-interactive)
- Does **NOT** source `.bashrc` by default (even with `BASH_ENV` in some configurations)
- Exports inside `bash -c "..."` do **NOT** always propagate to child processes due to quoting/subprocess isolation

**Symptoms**: `os.environ.get('API_KEY')` returns empty even after `source ~/.bashrc` succeeds.

**Why**: The Python process inside `bash -c` runs in a child scope that may not inherit the exported variables due to how this environment spawns subprocesses. The `echo` inside bash works, but `python3` cannot see them.

**Reliable workaround**: Don't rely on `bash -c` to propagate env vars to Python. Instead, use one of these:
1. **File-based fallback** (best): Store API keys in a local file (e.g., `.api_key`) and have Python read it as fallback when `os.environ` is empty.
2. **Hardcode at call site**: Pass the key as a parameter when calling the subprocess.
3. **Never put secrets in `.bashrc` for automated systems** — use a dedicated secrets manager or file.

### `.bashrc` Exports Are Not Reliable for Subprocesses
Even when `.bashrc` exports are placed **before** the `case $- in ... esac` guard, non-interactive bash (`bash -c`) may still not source `.bashrc` at all (it depends on how bash was invoked). In Hermes Agent's environment, `bash -c` does NOT source `.bashrc` by default.

**Rule of thumb**: Never rely on `.bashrc` exports for any automated process (CI, cron, subprocess, daemon). Use `/etc/environment` or a file-based fallback instead.

## Session Details
- [Ubuntu .bashrc non-interactive trap](references/ubuntu-bashrc-trap.md) — full case study from 2026-05-08 session with Semantic Scholar API key (Hermes Agent subprocess isolation discovered)
- [API Key Fallback Pattern](references/api-key-fallback-pattern.md) — file-based fallback for API keys in subprocess contexts (most reliable for automated systems)
- [SS Dual-Key Failover](references/ss-dual-key-failover.md) — dual-key ring + automatic failover for Semantic Scholar API (iYTNXX legacy + s2k- prefix, key rotation on failure)

## 验证清单 · VERIFICATION

- [ ] 缺失变量已定位消失点（.bashrc 守卫/引号边界/从未设置）
- [ ] 修复方案按可靠性排序给出（export 前移/BASH_ENV/.api_key 文件回退）
- [ ] 已区分非交互 shell 与 subprocess 边界导致的变量丢失
- [ ] 修复后变量在目标 shell 可用
- [ ] 输出含 diagnosis 与 fix 两段，无凭记忆断言
## 约束规则 · RULES

1. **输入约束**: 参数类型、范围、格式必须校验
2. **输出约束**: 返回值结构、编码、命名必须一致
3. **异常约束**: 错误信息必须包含上下文和恢复建议
4. **安全约束**: 不执行未验证的任意代码，不暴露内部状态

## Golden 集合 · GOLDEN SET

- **Golden Input**: 标准输入样本（覆盖正常路径）
- **Golden Output**: 预期输出（精确匹配或格式校验）
- **Golden Error**: 预期错误信息（覆盖失败路径）

> Golden 集合是测试的单一真理来源。所有改进必须通过 golden 测试。

> 违反规则的操作视为不安全，必须拒绝或隔离。

> 每项验证必须可执行、可记录、可复现。验证失败时记录原因和修复。

# Debug Env Variables---






> (P032 去重: 以下为合并前第二份中的 1 行独有内容, 保留以防丢失)
# Debug Env Variables

