Hmbown/CodeWhale · error
invalid environment placeholder in MCP config value
Error message
invalid environment placeholder in MCP config value
What it means
The placeholder parser found ${...} but the inner name fails validation: empty (${}) or containing characters outside ASCII letters, digits, and underscore. Names must be plain identifiers so environment lookup is unambiguous; substitution syntaxes from other tools are not supported.
Source
Thrown at crates/tui/src/mcp.rs:81
///
/// On a missing or malformed placeholder the error names only the offending
/// variable, never the surrounding value, so a secret-bearing string is never
/// echoed into logs or error output.
fn expand_env_placeholders_with(
value: &str,
environment: Option<&crate::plugins::HostEnvironment>,
) -> Result<String> {
let mut out = String::new();
let mut rest = value;
while let Some(start) = rest.find("${") {
out.push_str(&rest[..start]);
let after = &rest[start + 2..];
let Some(end) = after.find('}') else {
anyhow::bail!("unterminated environment placeholder in MCP config value");
};
let name = &after[..end];
if name.is_empty() || !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
anyhow::bail!("invalid environment placeholder in MCP config value");
}
let env_value = environment
.map_or_else(|| std::env::var(name), |env| env.var(name))
.with_context(|| {
format!("environment variable {name} required by MCP config is not set")
})?;
out.push_str(&env_value);
rest = &after[end + 1..];
}
out.push_str(rest);
Ok(out)
}
#[cfg(test)]
fn expand_env_placeholders(value: &str) -> Result<String> {
expand_env_placeholders_with(value, None)
}
View on GitHub (pinned to 8880682c63)
Solutions
- Use plain identifier names: letters, digits, underscore only, e.g. ${MY_VAR}
- Express defaults by exporting the variable in the launching environment instead of inline default syntax
- If you control the variable, rename it to an identifier-safe name
Example fix
# before
"url": "https://${host-name}/mcp"
# after: identifier-safe name, exported in the environment
export MCP_HOST_NAME=api.example.com
"url": "https://${MCP_HOST_NAME}/mcp" Defensive patterns
Strategy: validation
Validate before calling
// Validate placeholder names at config load time
fn placeholder_names_ok(v: &str) -> bool {
let mut rest = v;
while let Some(start) = rest.find("${") {
let after = &rest[start + 2..];
let Some(end) = after.find('}') else { return false };
let name = &after[..end];
if name.is_empty() || !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
return false;
}
rest = &after[end + 1..];
}
true
} Prevention
- Use UPPER_SNAKE environment variable names exclusively
- Do not port shell or docker substitution syntax into MCP configs
- Verify referenced variables are exported in the launching environment
When it happens
Trigger: Values like ${}, ${MY-VAR}, ${VAR:-default}, ${var/cmd}, or placeholders containing whitespace - any inner text that is not a bare ASCII identifier.
Common situations: Copying docker-compose or bash substitution syntax (${VAR:-default}) into MCP config, hyphenated variable names, placeholders mangled by copy-paste from documentation.
Related errors
- unterminated environment placeholder in MCP config value
- MCP config path cannot be empty
- MCP config path cannot contain '..' components
- Unsupported MCP transport '{transport}'. Supported values: s
- MCP server '{name}' config must have either 'command' or 'ur
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/1ada6116dc085e0d.
Report an issue: GitHub.