{"record":{"id":"503ba1443f70aaa0","repo":"astral-sh/ruff","slug":"permissiondenied","errorCode":"PermissionDenied","errorMessage":"external commands are disabled in an untrusted workspace","messagePattern":"external commands are disabled in an untrusted workspace","errorType":"error_code","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/ty_server/src/system.rs","lineNumber":328,"sourceCode":"    where\n        D: Deserializer<'de>,\n    {\n        // The LSP option is `untrustedWorkspace`, so `true` means untrusted.\n        Ok(match Option::<bool>::deserialize(deserializer)? {\n            Some(true) => Self::Untrusted,\n            Some(false) | None => Self::Trusted,\n        })\n    }\n}\n\n/// Rejects commands without retaining the LSP document index.\nstruct UntrustedWorkspaceExecutor;\n\nimpl CommandExecutor for UntrustedWorkspaceExecutor {\n    fn execute(&self, _command: Command) -> Result<Output> {\n        Err(std::io::Error::new(\n            std::io::ErrorKind::PermissionDenied,\n            \"external commands are disabled in an untrusted workspace\",\n        ))\n    }\n\n    fn dyn_clone(&self) -> Box<dyn CommandExecutor> {\n        Box::new(Self)\n    }\n}\n\nfn not_a_text_document(path: impl Display) -> std::io::Error {\n    std::io::Error::new(\n        std::io::ErrorKind::InvalidInput,\n        format!(\"Input is not a text document: {path}\"),\n    )\n}\n\nfn virtual_path_not_found(path: impl Display) -> std::io::Error {\n    std::io::Error::new(\n        std::io::ErrorKind::NotFound,","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/astral-sh/ruff/blob/d1087a4b9e03d253a88703f34e0869ee4b805456/crates/ty_server/src/system.rs#L310-L346","documentation":"ty's server wraps all OS access in a System trait. When the client reports the workspace as untrusted, LspSystem::command_executor returns UntrustedWorkspaceExecutor, whose execute() rejects every Command with io::ErrorKind::PermissionDenied and this message. It is a deliberate security boundary so untrusted workspace code cannot spawn processes.","triggerScenarios":"The workspace trust state is Untrusted (client marks the folder untrusted) and any ty feature that shells out calls Command::output through the executor - e.g. plugin discovery/execution or other subprocess-based tooling. Every spawn attempt gets PermissionDenied instead of running.","commonSituations":"Opening a folder in VS Code 'Restricted Mode' with the ty extension enabled; servers initialized with workspace trust disabled; hardened CI setups that never grant trust.","solutions":["Mark the workspace as trusted in the editor (VS Code: Manage Workspace Trust) and reload/reinitialize the ty server","Disable the subprocess-dependent feature (e.g. plugins) for untrusted workspaces","If you implement a client, only enable command-requiring settings when you report trust"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// client side: only enable subprocess-dependent features when trust is granted\nconst canRunCommands = workspaceIsTrusted; // from the editor's trust prompt\nsettings.plugins = canRunCommands ? settings.plugins : null;","typeGuard":null,"tryCatchPattern":"match err.kind() {\n    std::io::ErrorKind::PermissionDenied => { /* disable command-based feature, continue without it */ }\n    kind => return Err(err),\n}","preventionTips":["Decide workspace trust before enabling ty features that shell out","Treat PermissionDenied from the executor as 'feature unavailable', not a fatal error","In CI, run ty in trusted sandboxes when subprocess features are required"],"tags":["lsp","workspace-trust","permissions","subprocess","security"],"backgroundTag":"permission-denied-sandboxed-executor","analyzedSha":"d1087a4b9e03d253a88703f34e0869ee4b805456","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}