BloopAI/vibe-kanban · error
UTF-8 after stripping ANSI
Error message
UTF-8 after stripping ANSI
What it means
The BACKEND_PORT/PORT env var is sanitized by stripping ANSI escape codes before parsing as u16. If, after stripping, the bytes are still not valid UTF-8, String::from_utf8 fails and the .expect("UTF-8 after stripping ANSI") panics with this message. It is a defensive check: ANSI stripping can split a multi-byte UTF-8 sequence.
Source
Thrown at crates/server/src/main.rs:102
deployment
.container()
.backfill_repo_names()
.await
.map_err(DeploymentError::from)?;
deployment
.track_if_analytics_allowed("session_start", serde_json::json!({}))
.await;
// Preload global executor options cache for all executors with DEFAULT presets
tokio::spawn(async move {
executors::executors::utils::preload_global_executor_options_cache().await;
});
let port = std::env::var("BACKEND_PORT")
.or_else(|_| std::env::var("PORT"))
.ok()
.and_then(|s| {
// Remove any ANSI codes, then turn into String
let cleaned =
String::from_utf8(strip(s.as_bytes())).expect("UTF-8 after stripping ANSI");
cleaned.trim().parse::<u16>().ok()
})
.unwrap_or_else(|| {
tracing::info!("No PORT environment variable set, using port 0 for auto-assignment");
0
}); // Use 0 to find free port if no specific port provided
let proxy_port = std::env::var("PREVIEW_PROXY_PORT")
.ok()
.and_then(|s| s.trim().parse::<u16>().ok())
.unwrap_or(0);
let host = std::env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
let main_listener = tokio::net::TcpListener::bind(format!("{host}:{port}")).await?;
let actual_main_port = main_listener.local_addr()?.port();
let proxy_listener = tokio::net::TcpListener::bind(format!("{host}:{proxy_port}")).await?;View on GitHub (pinned to 4deb7eca8f)
Solutions
- Inspect the variable's raw bytes (echo -n "$BACKEND_PORT" | xxd) and rewrite it as plain ASCII digits.
- Re-export cleanly: export BACKEND_PORT=3001 with no control characters.
- Unset the variable so the code falls back to auto-assigning port 0.
- Fix the launcher script or process manager embedding ANSI codes in the value.
Defensive patterns
Strategy: validation
Validate before calling
fn parse_port(raw: &str) -> Option<u16> {
let cleaned = String::from_utf8(strip(raw.as_bytes())).ok()?;
cleaned.trim().parse::<u16>().ok()
}
// pre-check before launch:
if let Ok(v) = std::env::var("BACKEND_PORT").or_else(|_| std::env::var("PORT")) {
if parse_port(&v).is_none() && !v.trim().is_empty() {
eprintln!("PORT/BACKEND_PORT is not a valid port: {v:?}");
}
} Type guard
fn is_ascii_port(s: &str) -> bool {
!s.is_empty()
&& s.bytes().all(|b| b.is_ascii_digit())
&& s.parse::<u16>().is_ok()
} Try / catch
let port = std::env::var("BACKEND_PORT")
.or_else(|_| std::env::var("PORT"))
.ok()
.and_then(|s| String::from_utf8(strip(s.as_bytes())).ok())
.and_then(|c| c.trim().parse::<u16>().ok())
.unwrap_or(0); // fall back to auto-assign instead of panicking Prevention
- Set PORT/BACKEND_PORT to plain ASCII digits; avoid copy-pasting from ANSI-styled terminals.
- Validate env vars with a startup config check that fails fast with a clear message.
- Audit launcher scripts/process managers for ANSI injection into env values.
- Use .ok() chaining rather than expect when sanitizing external input.
When it happens
Trigger: BACKEND_PORT or PORT contains binary/non-UTF-8 bytes such that strip() leaves an invalid UTF-8 sequence — e.g. the env var was set from raw bytes, a script emitted garbage, or a truncated ANSI sequence split a multi-byte character.
Common situations: Port injected by a process manager or shell wrapper that embeds ANSI codes; truncated or corrupted environment variable at process spawn; copy-pasted control characters into a config file or shell export; Windows console encoding artifacts.
Related errors
- Failed to load orchestrator MCP context from /api/containers
- no OAuth providers configured
- Migration failed: {}
- Migration DB pool error: {}
- Failed to copy database file
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/06d60d07993872d0.
Report an issue: GitHub.