astral-sh/ruff · error · std::io::Error
PermissionDenied
PermissionDenied
Error message
external commands are disabled in an untrusted workspace
What it means
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.
Source
Thrown at crates/ty_server/src/system.rs:328
where
D: Deserializer<'de>,
{
// The LSP option is `untrustedWorkspace`, so `true` means untrusted.
Ok(match Option::<bool>::deserialize(deserializer)? {
Some(true) => Self::Untrusted,
Some(false) | None => Self::Trusted,
})
}
}
/// Rejects commands without retaining the LSP document index.
struct UntrustedWorkspaceExecutor;
impl CommandExecutor for UntrustedWorkspaceExecutor {
fn execute(&self, _command: Command) -> Result<Output> {
Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"external commands are disabled in an untrusted workspace",
))
}
fn dyn_clone(&self) -> Box<dyn CommandExecutor> {
Box::new(Self)
}
}
fn not_a_text_document(path: impl Display) -> std::io::Error {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Input is not a text document: {path}"),
)
}
fn virtual_path_not_found(path: impl Display) -> std::io::Error {
std::io::Error::new(
std::io::ErrorKind::NotFound,View on GitHub (pinned to d1087a4b9e)
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
Defensive patterns
Strategy: fallback
Validate before calling
// client side: only enable subprocess-dependent features when trust is granted const canRunCommands = workspaceIsTrusted; // from the editor's trust prompt settings.plugins = canRunCommands ? settings.plugins : null;
Try / catch
match err.kind() {
std::io::ErrorKind::PermissionDenied => { /* disable command-based feature, continue without it */ }
kind => return Err(err),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/503ba1443f70aaa0.
Report an issue: GitHub.