zeroclaw-labs/zeroclaw · error
Edge TTS binary_path must be one of {:?}, got: {raw_path}
Error message
Edge TTS binary_path must be one of {:?}, got: {raw_path} What it means
After the path-separator check, EdgeTtsProvider::new requires binary_path to be exactly one of ALLOWED_BINARIES = ["edge-tts", "edge-playback"]. Together with the separator rule this closes the allowlist: config cannot make the runtime execute any other binary name, so a hostile config edit cannot swap in an arbitrary command.
Source
Thrown at crates/zeroclaw-channels/src/tts.rs:651
}
impl EdgeTtsProvider {
/// Allowed basenames for the Edge TTS binary.
const ALLOWED_BINARIES: &[&str] = &["edge-tts", "edge-playback"];
pub fn new(alias: &str, config: &TtsProviderConfig) -> Result<Self> {
let raw_path = config
.binary_path
.clone()
.filter(|p| !p.trim().is_empty())
.unwrap_or_else(|| "edge-tts".to_string());
if raw_path.contains('/') || raw_path.contains('\\') {
bail!(
"Edge TTS binary_path must be a bare command name without path separators, got: {raw_path}"
);
}
if !Self::ALLOWED_BINARIES.contains(&raw_path.as_str()) {
bail!(
"Edge TTS binary_path must be one of {:?}, got: {raw_path}",
Self::ALLOWED_BINARIES,
);
}
Ok(Self {
alias: alias.to_string(),
binary_path: raw_path,
#[cfg(test)]
binary_args: Vec::new(),
timeout: TTS_HTTP_TIMEOUT,
})
}
/// Test-only constructor that accepts a script path and timeout so tests
/// can drive the `edge-tts` subprocess. The production [`new`](Self::new)
/// allowlist stays a security boundary; this exists only in Unix test builds.
#[cfg(all(test, unix))]
fn new_with_binary(alias: &str, binary_path: &str, timeout: std::time::Duration) -> Self {View on GitHub (pinned to 88bb9c8533)
Solutions
- Use exactly "edge-tts" (default) or "edge-playback".
- On Windows, install the pip package (`pip install edge-tts`); the .exe is then found by its bare name through PATH.
- If you need a pinned/wrapped binary, expose it under the exact name "edge-tts" earlier in PATH instead of renaming it in config.
Example fix
# before [providers.tts.edge.main] binary_path = "edge-tts.exe" # after [providers.tts.edge.main] binary_path = "edge-tts"
Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED: &[&str] = &["edge-tts", "edge-playback"];
if let Some(bin) = &edge_cfg.binary_path {
assert!(ALLOWED.contains(&bin.as_str()), "binary_path must be one of {ALLOWED:?}");
} Type guard
fn is_allowed_edge_binary(p: &str) -> bool {
matches!(p, "edge-tts" | "edge-playback")
} Prevention
- Write the bare exact strings "edge-tts" or "edge-playback"; no .exe suffix, no renames.
- On Windows, rely on PATH resolution of the pip-installed edge-tts.exe.
- For pinned versions, wrap in a script named exactly edge-tts placed earlier in PATH.
When it happens
Trigger: binary_path = "edge-tts.exe" (Windows suffix), "edge_tts" (underscore), a renamed wrapper script like "edge-tts-custom", or a package-manager alias. Everything except the two exact strings is rejected at construction time.
Common situations: Windows users write the .exe suffix out of habit. Teams rename a pinned wrapper for version control and forget the allowlist. The error names the allowed values verbatim, so the fix is mechanical.
Understand the failure class
Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.
Related errors
- Edge TTS binary_path must be a bare command name without pat
- jira.allowed_actions contains unknown action: '{}'. Valid: g
- Destination {} is not in allowed list
- matrix: `homeserver` is required
- Cannot persist empty Telegram identity
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/22d7652b8f6753a3.
Report an issue: GitHub.