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

  1. Replace every ${VAR}/$VAR reference with a literal value in the workspace .env
  2. If a value must come from the environment, export it in the shell before launching codewhale instead of expanding inside .env
  3. Keep compose-style interpolated files under a different filename that codewhale does not load
  4. 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

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


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/3ea05322ae25bb07. Report an issue: GitHub.