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

  1. Mark the workspace as trusted in the editor (VS Code: Manage Workspace Trust) and reload/reinitialize the ty server
  2. Disable the subprocess-dependent feature (e.g. plugins) for untrusted workspaces
  3. 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

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.