{"record":{"id":"bfc85af0faa6288c","repo":"Kuberwastaken/claurst","slug":"invalid-contains-unsafe-characters","errorCode":null,"errorMessage":"Invalid {}: contains unsafe characters","messagePattern":"Invalid (.+?): contains unsafe characters","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-rust/crates/bridge/src/lib.rs","lineNumber":231,"sourceCode":"        }\r\n\r\n        config\r\n    }\r\n\r\n    /// Returns `true` only when the bridge is both enabled and has a token.\r\n    pub fn is_active(&self) -> bool {\r\n        self.enabled && self.session_token.is_some()\r\n    }\r\n\r\n    /// Validate that a server-provided ID is safe to interpolate into a URL\r\n    /// path segment. Prevents path traversal (e.g. `../../admin`).\r\n    ///\r\n    /// Mirrors `validateBridgeId()` in `bridgeApi.ts`.\r\n    pub fn validate_id<'a>(id: &'a str, label: &str) -> anyhow::Result<&'a str> {\r\n        static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();\r\n        let re = RE.get_or_init(|| regex::Regex::new(r\"^[a-zA-Z0-9_-]+$\").unwrap());\r\n        if id.is_empty() || !re.is_match(id) {\r\n            anyhow::bail!(\"Invalid {}: contains unsafe characters\", label);\r\n        }\r\n        Ok(id)\r\n    }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Permission decision\r\n// ---------------------------------------------------------------------------\r\n\r\n/// A tool-use permission decision sent by the web UI back to the CLI.\r\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\r\n#[serde(rename_all = \"snake_case\")]\r\npub enum PermissionDecision {\r\n    Allow,\r\n    AllowPermanently,\r\n    Deny,\r\n    DenyPermanently,\r\n}\r","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/bridge/src/lib.rs#L213-L249","documentation":"`run_mcp_auth_session` calls the `open` crate's `open::that(&session.auth_url)` to launch the user's default browser at the OAuth authorization URL. If spawning the browser fails, the OS error is wrapped in this anyhow message. The OAuth flow cannot proceed because no browser can display the consent page.","triggerScenarios":"`open::that()` returns Err — no default browser configured, the browser executable is missing, the process cannot be spawned (headless server, SSH session without display), or the OS denies launching the handler.","commonSituations":"Running the auth flow over SSH or in a headless CI/container with no `$BROWSER`/desktop environment; macOS/Linux default-browser association broken; restricted sandbox blocking process spawn.","solutions":["Run the auth flow in a desktop session where a default browser is configured, or set the `BROWSER` env var to a usable browser binary.","Manually copy the auth URL from logs/output into a browser on any machine, then complete the flow (the local listener still waits for the callback).","Check the wrapped OS error message to identify the missing browser/handler.","On servers, use an SSH tunnel for the callback port so a browser on another host can reach the redirect URI."],"exampleFix":"// before: fails headless because open::that cannot spawn a browser\n// after: fall back to printing the URL for manual opening\nif let Err(e) = open::that(&session.auth_url) {\n    eprintln!(\"Failed to open browser for OAuth: {}\", e);\n    eprintln!(\"Open this URL manually: {}\", session.auth_url);\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = open::that(&session.auth_url) {\n    eprintln!(\"Could not open browser: {}. Open manually:\\n{}\", e, session.auth_url);\n}","preventionTips":["Detect headless environments (no DISPLAY/BROWSER) and print the URL instead of opening a browser","Set the BROWSER env var on servers used for auth","Document that the callback listener keeps waiting, so the URL can be opened on another machine"],"tags":["oauth","browser","process-spawn","headless"],"backgroundTag":"command-not-found","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}