Hmbown/CodeWhale · error · anyhow::Error
{} uses variable expansion; workspace .env values must be li
Error message
{} uses variable expansion; workspace .env values must be literal to prevent ambient-secret substitution What it means
Workspace .env credentials are loaded through load_workspace_dotenv_credentials_from_path on top of read_stable_workspace_dotenv, which accepts only literal KEY=value lines. dotenv_has_variable_expansion detects ${VAR}/$VAR interpolation syntax and rejects the whole file: expansion would let ambient environment variables substitute into the session's credential values, defeating the workspace-ownership model.
Source
Thrown at crates/tui/src/lib.rs:2516
return Err(anyhow!(
"could not inspect {}: {error}",
candidate.display()
));
}
}
if ancestor == boundary {
break;
}
}
Ok(None)
}
fn load_workspace_dotenv_credentials_from_path(path: &Path) -> Result<WorkspaceDotenvReport> {
let contents = read_stable_workspace_dotenv(path)?;
let text = std::str::from_utf8(&contents)
.map_err(|_| anyhow!("{} is not valid UTF-8", path.display()))?;
if dotenv_has_variable_expansion(text) {
bail!(
"{} uses variable expansion; workspace .env values must be literal to prevent ambient-secret substitution",
path.display()
);
}
let mut report = WorkspaceDotenvReport {
path: path.to_path_buf(),
..WorkspaceDotenvReport::default()
};
let entries = dotenvy::from_read_iter(std::io::Cursor::new(contents))
.collect::<std::result::Result<Vec<_>, _>>()
.map_err(|_| anyhow!("{} could not be parsed safely", path.display()))?;
for entry in entries {
let (key, value) = entry;
if !is_workspace_dotenv_credential_key(&key) {
report.ignored.insert(key);
continue;
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Replace every ${VAR}/$VAR reference with a literal value in the workspace .env
- If a value must come from the environment, export it in the shell before launching codewhale instead of expanding inside .env
- Keep compose-style interpolated files under a different filename that codewhale does not load
- Run a quick scan: grep -E '\$\{|\$[A-Za-z]' .env should find nothing
Example fix
# before (.env)
DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
OPENROUTER_KEY=$OR_KEY
# after (.env)
DEEPSEEK_API_KEY=sk-literal-value
OPENROUTER_KEY=sk-or-literal-value Defensive patterns
Strategy: validation
Validate before calling
# Reject interpolated .env before codewhale loads it
if grep -Eq '\$\{|\$[A-Za-z_]' .env 2>/dev/null; then
echo '.env uses variable expansion; replace with literal values'; exit 2
fi Prevention
- Keep workspace .env strictly literal KEY=value
- Export environment-provided secrets in the shell instead of expanding them in .env
- Keep compose-style interpolated env files under another name
When it happens
Trigger: A workspace .env containing lines like `API_KEY=${HOME_SECRET}` or `KEY=$OTHER` at the moment Codewhale loads workspace dotenv credentials (exec/serve flows that read the workspace .env).
Common situations: Copying a docker-compose style .env that relies on interpolation; sharing one .env across tools where compose-style expansion is expected; templated .env files committed from a skeleton.
Related errors
- {} is not a regular file
- {} exceeds the {} byte workspace .env limit
- persistent allow rules must be scoped to a workspace
- project workspace path cannot contain '..' components
- {} has multiple filesystem links, not a unique workspace-own
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/3ea05322ae25bb07.
Report an issue: GitHub.