Hmbown/CodeWhale · error · anyhow::Error
Invalid tui.alternate_screen '{mode}': expected auto, always
Error message
Invalid tui.alternate_screen '{mode}': expected auto, always, or never. What it means
Config::validate() requires tui.alternate_screen to be auto, always, or never. Unlike the neighboring checks, the value is lowercased but NOT trimmed, so leading/trailing whitespace also fails. Boolean-style strings are the most common mistake because the key sounds boolean.
Source
Thrown at crates/tui/src/config.rs:4448
}
}
if let Some(mode) = self.sandbox_mode.as_deref() {
let normalized = mode.trim().to_ascii_lowercase();
if !matches!(
normalized.as_str(),
"read-only" | "workspace-write" | "danger-full-access" | "external-sandbox"
) {
anyhow::bail!(
"Invalid sandbox_mode '{mode}': expected read-only, workspace-write, danger-full-access, or external-sandbox."
);
}
}
if let Some(tui) = &self.tui
&& let Some(mode) = tui.alternate_screen.as_deref()
{
let mode = mode.to_ascii_lowercase();
if !matches!(mode.as_str(), "auto" | "always" | "never") {
anyhow::bail!(
"Invalid tui.alternate_screen '{mode}': expected auto, always, or never."
);
}
}
if let Some(auto_review) = &self.auto_review {
auto_review.validate()?;
}
if let Some(providers) = &self.providers {
providers.validate()?;
}
Ok(())
}
#[must_use]
pub fn api_provider(&self) -> ApiProvider {
// #1519 safety fix: when `provider = "<name>"` is not a built-in provider
// but names a `[providers.<name>]` custom table, route as the dynamic
// custom identity. Exact configured keys win even when their spellingView on GitHub (pinned to 8880682c63)
Solutions
- Set alternate_screen = "auto" | "always" | "never" (string, not boolean).
- Ensure no leading/trailing whitespace in the value.
- If the terminal wrapper mishandles the alternate screen, "never" is usually the intended fix.
Example fix
# before [tui] alternate_screen = "false" # after [tui] alternate_screen = "never"
Defensive patterns
Strategy: validation
Validate before calling
fn alternate_screen_is_valid(raw: &str) -> bool {
// mirror the exact check: lowercase only, NO trim
matches!(raw.to_ascii_lowercase().as_str(), "auto" | "always" | "never")
}
if let Some(tui) = &config.tui {
if let Some(mode) = tui.alternate_screen.as_deref() {
anyhow::ensure!(alternate_screen_is_valid(mode), "tui.alternate_screen must be auto/always/never");
}
} Type guard
fn is_valid_alternate_screen(raw: &str) -> bool {
matches!(raw.to_ascii_lowercase().as_str(), "auto" | "always" | "never")
} Try / catch
match config.validate() {
Err(e) if e.to_string().starts_with("Invalid tui.alternate_screen") => {
config.tui.as_mut().map(|t| t.alternate_screen = None);
config.validate()?
}
other => other,
} Prevention
- Use the string keywords auto/always/never — never booleans.
- Watch for stray whitespace: this field is lowercased but not trimmed.
- When embedding in terminal wrappers, test with the actual wrapper before committing config.
When it happens
Trigger: Setting [tui] alternate_screen = "true"/"false"/"yes"; a value with stray whitespace such as "always " (not trimmed by this check); copy from an older config schema that used booleans.
Common situations: Users assume a display toggle accepts true/false; terminal-wrapper scripts (tmux, emacs) prompt users to disable the alternate screen and they guess the wrong vocabulary.
Related errors
- Invalid default_text_model '{model}' for provider '{}': expe
- Invalid approval_policy '{policy}': expected on-request, unt
- Invalid verbosity '{v}': expected normal or concise.
- Invalid sandbox_mode '{mode}': expected read-only, workspace
- unavailable credential
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/89828c75376ba82d.
Report an issue: GitHub.