{"record":{"id":"06d60d07993872d0","repo":"BloopAI/vibe-kanban","slug":"utf-8-after-stripping-ansi","errorCode":null,"errorMessage":"UTF-8 after stripping ANSI","messagePattern":"UTF-8 after stripping ANSI","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/server/src/main.rs","lineNumber":102,"sourceCode":"    deployment\n        .container()\n        .backfill_repo_names()\n        .await\n        .map_err(DeploymentError::from)?;\n    deployment\n        .track_if_analytics_allowed(\"session_start\", serde_json::json!({}))\n        .await;\n    // Preload global executor options cache for all executors with DEFAULT presets\n    tokio::spawn(async move {\n        executors::executors::utils::preload_global_executor_options_cache().await;\n    });\n    let port = std::env::var(\"BACKEND_PORT\")\n        .or_else(|_| std::env::var(\"PORT\"))\n        .ok()\n        .and_then(|s| {\n            // Remove any ANSI codes, then turn into String\n            let cleaned =\n                String::from_utf8(strip(s.as_bytes())).expect(\"UTF-8 after stripping ANSI\");\n            cleaned.trim().parse::<u16>().ok()\n        })\n        .unwrap_or_else(|| {\n            tracing::info!(\"No PORT environment variable set, using port 0 for auto-assignment\");\n            0\n        }); // Use 0 to find free port if no specific port provided\n\n    let proxy_port = std::env::var(\"PREVIEW_PROXY_PORT\")\n        .ok()\n        .and_then(|s| s.trim().parse::<u16>().ok())\n        .unwrap_or(0);\n\n    let host = std::env::var(\"HOST\").unwrap_or_else(|_| \"127.0.0.1\".to_string());\n\n    let main_listener = tokio::net::TcpListener::bind(format!(\"{host}:{port}\")).await?;\n    let actual_main_port = main_listener.local_addr()?.port();\n\n    let proxy_listener = tokio::net::TcpListener::bind(format!(\"{host}:{proxy_port}\")).await?;","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/server/src/main.rs#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn parse_port(raw: &str) -> Option<u16> {\n    let cleaned = String::from_utf8(strip(raw.as_bytes())).ok()?;\n    cleaned.trim().parse::<u16>().ok()\n}\n// pre-check before launch:\nif let Ok(v) = std::env::var(\"BACKEND_PORT\").or_else(|_| std::env::var(\"PORT\")) {\n    if parse_port(&v).is_none() && !v.trim().is_empty() {\n        eprintln!(\"PORT/BACKEND_PORT is not a valid port: {v:?}\");\n    }\n}","typeGuard":"fn is_ascii_port(s: &str) -> bool {\n    !s.is_empty()\n        && s.bytes().all(|b| b.is_ascii_digit())\n        && s.parse::<u16>().is_ok()\n}","tryCatchPattern":"let port = std::env::var(\"BACKEND_PORT\")\n    .or_else(|_| std::env::var(\"PORT\"))\n    .ok()\n    .and_then(|s| String::from_utf8(strip(s.as_bytes())).ok())\n    .and_then(|c| c.trim().parse::<u16>().ok())\n    .unwrap_or(0); // fall back to auto-assign instead of panicking","preventionTips":["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."],"tags":["environment","encoding","utf-8","ansi","startup"],"backgroundTag":"invalid-utf8-env-var","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}