# The Ruff VS Code extension finds its binary through your Python environment, then falls back

> ruff-vscode is the Visual Studio Code extension for the Ruff linter and formatter, and it ships with a pinned ruff==0.17.0. Its binary resolution chain, its safe versus unsafe fix split and its untrusted workspace restrictions are where the real behaviour lives.

**astral-sh/ruff-vscode** — A Visual Studio Code extension with support for the Ruff linter.

- Repository: https://github.com/astral-sh/ruff-vscode
- Stars: 1,679 · Forks: 80
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/astral-sh-ruff-vscode

## Binary resolution: environment first, PATH second, bundled last

The extension does not require any other VS Code extension, but the README recommends one of two: the Python Environments extension, which itself needs VS Code 1.100 or later, or a version of the Python extension that supports Python 3.8 or later.

When either is available, the Ruff extension uses it to locate the Ruff binary in the active environment. If no binary turns up there, or neither extension is present, it falls back to a Ruff binary found on the PATH, and then to the one bundled with the extension. Reload VS Code after installing either recommended extension so environment detection activates.

That three-step chain is the single most useful thing to understand about this extension. Each fallback is quieter than the last, and the bundled binary is always present, so a misconfigured interpreter does not produce an error; it produces Ruff running at a version you did not choose.

The bundled version is pinned. Both the README and the extension metadata state that the extension ships with ruff==0.17.0, and the repository's own pyproject.toml lists dependencies as ruff==0.17.0, with a generated requirements.txt pinning the same version by hash.

## Safe fixes are applied automatically, unsafe fixes are not

Ruff classifies its automatic fixes as safe and unsafe. By default the Fix all action applies only the safe ones. Unsafe fixes still work, but through the individual Quick fix action where you select them deliberately.

If you want Fix all to sweep everything, the README points to a configuration change rather than an extension setting:

```toml
unsafe-fixes = true
```

That key goes in your Ruff configuration file, alongside extend-include and everything else Ruff itself reads. The consequence is that the extension has no separate switch for this, so the behaviour of a shared on-save fix is decided by whatever configuration file is discovered for the workspace.

This is the part that surprises people migrating from Flake8 plugins. The automatic fixes here are edits to source, applied while you type, and the safe/unsafe split is the only thing standing between a typo fix and a semantic change you did not review.

## What triggers the extension, and which files count as configuration

Ruff runs automatically when you open or edit a Python or Jupyter Notebook file, or one of the Ruff configuration files: pyproject.toml, ruff.toml or .ruff.toml.

The activation events in the manifest are broader than that description. Besides onLanguage:python and onLanguage:markdown, the extension activates on workspaceContains entries for *.py, *.ipynb, *.md, pyproject.toml, ruff.toml and .ruff.toml at any depth. So a workspace containing only Markdown files or a nested pyproject.toml will start the extension before you open any Python.

To turn Ruff off you disable the extension per workspace through the standard Visual Studio Code extension controls. There is no separate toggle for the language server in the settings documented here.

Markdown activation is not cosmetic: the extension exposes formatting for Python code blocks embedded in Markdown, which is configured separately below.

## Format on save for Python, notebooks and Markdown code blocks

Formatting is wired through the standard VS Code format-on-save setting plus the formatter identifier charliermarsh.ruff. For Python:

```json
{
  "[python]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "charliermarsh.ruff"
  }
}
```

Notebooks need their own format-on-save key alongside the same default formatter:

```json
{
  "notebook.formatOnSave.enabled": true,
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff"
  }
}
```

Markdown code blocks take a separate language block, which is what makes Python inside README files format on save too:

```json
{
  "[markdown]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "charliermarsh.ruff"
  }
}
```

Fixing on save works the same way, through source.fixAll. For Python:

```json
{
  "[python]": {
    "editor.codeActionsOnSave": {
      "source.fixAll": "explicit"
    }
  }
}
```

Notebooks have a distinct action name, notebook.source.fixAll, and importing organization uses source.organizeImports, which the README describes as isort-compatible.

## Notebook support arrived with LSP 3.17 and stopped needing configuration

Notebook support rests on the Notebook Document Synchronization capability introduced in version 3.17 of the Language Server Protocol, and it covers diagnostics, code actions and formatting.

The relevant change for users is a default. Jupyter Notebook support was stabilized in Ruff 0.6.0, and notebooks are now linted and formatted without being listed explicitly. On earlier versions you needed extend-include in the configuration file:

```toml
[tool.ruff]
extend-include = ["*.ipynb"]
```

That key is still worth knowing, because it is the mechanism for including other file types generally, and older pinned versions of Ruff inside a project will still need it even if the extension is current.

## Untrusted workspaces drop your settings and pin the bundled executable

Support for untrusted workspaces arrived in v2024.32.0, and the behaviour is restrictive by design. In an untrusted workspace the extension always uses the bundled Ruff executable, regardless of any executable or Python environment settings. From Ruff 0.17.0 onward, formatting there always uses Ruff's built-in formatter rather than the uv backend.

Five settings are declared unsupported in that mode, both in the README and in the manifest's restrictedConfigurations list: ruff.configuration, ruff.format.backend, ruff.importStrategy, ruff.interpreter and ruff.path.

ruff.configuration being restricted is the one with teeth. A checkout of an untrusted repository can carry a pyproject.toml with a Ruff configuration in it, and the extension will not honour it in untrusted mode. Diagnostics still run, but they run against defaults you did not choose.

Virtual workspaces are a separate matter: the manifest declares them unsupported outright, with a description stating they are not supported by the extension.

## Version cadence, toolchain floor and a licence GitHub cannot classify

The extension version follows a date-based scheme rather than semver: 2026.86.0 was released on 2026-10-09, after 2026.84.0 on 2026-09-24 and 2026.82.0 on 2026-09-16. The manifest version field matches the release tag, and the repository's pyproject.toml carries the same 2026.86.0.

The engine requirement is vscode ^1.91.0 with npm >=11.10.0 for building. Note the gap with the Python Environments extension, which needs VS Code 1.100 or later: on an editor between 1.91.0 and 1.99, you can install this extension but you cannot use the recommended environment detection path.

Licensing is worth reading directly. GitHub classifies the repository licence as Other, a custom licence it cannot categorise, and there is a LICENSE file in the repository root. The package.json metadata separately declares MIT as the extension's licence field. Both statements exist in the repository, and anyone redistributing should read the LICENSE file rather than relying on the metadata field.

The repository is not archived and the last push was on 2026-10-15.

## Where the extension is the wrong tool

Ruff itself is the alternative to this extension, and the README is explicit about the substitution: Ruff can replace Flake8 plus dozens of plugins, Black, isort and pyupgrade. That claim comes from the Ruff project, not from the extension, so treat it accordingly.

The real difference is where the checks run. In the editor, the extension is at the mercy of the resolution chain above, and its bundled binary at 0.17.0 can differ from your pin. A team that runs 0.15 in CI and accepts the editor results without checking the resolved version is reviewing edits produced by code their build never runs.

The extension also has no value outside VS Code, and the Manifest is honest about its platform edges: no virtual workspaces, restricted settings in untrusted workspaces. If your team edits over SSH remotes or in a browser-based workspace, those limits decide it.

## Conclusion

Adopt ruff-vscode if you want Ruff diagnostics, quick fixes and formatting inside the editor without running a separate CLI, and install the Python Environments extension alongside it so the binary resolution chain picks up your project interpreter. Skip it if you are on VS Code older than 1.91.0, or in virtual workspaces, which the manifest declares unsupported. Before trusting the green squiggles, check which executable the extension actually resolved, since a bundled fallback can silently lint with a different Ruff version than your project pins.

## FAQ

### Which version of Ruff does the ruff-vscode extension bundle?

The extension ships with ruff==0.17.0. The repository's pyproject.toml lists ruff==0.17.0 as a dependency and the generated requirements.txt pins the same version by hash.

### How does the Ruff extension find the Ruff binary?

It first uses the Python Environments extension or a supported Python extension to locate Ruff in the active environment. If nothing is found there, or neither extension is available, it falls back to a Ruff binary on the PATH and then to the bundled executable.

### Does the Ruff extension apply unsafe fixes with Fix all?

No. Fix all applies safe fixes by default. Unsafe fixes can still be applied one at a time with Quick fix, or applied by Fix all if you set unsafe-fixes = true in the Ruff configuration file.

### Which Ruff settings are ignored in an untrusted workspace?

ruff.configuration, ruff.format.backend, ruff.importStrategy, ruff.interpreter and ruff.path are all unsupported in an untrusted workspace, where the extension always uses the bundled Ruff executable instead.

### What VS Code version does the Ruff extension need?

The manifest declares vscode ^1.91.0. Separately, the recommended Python Environments extension requires VS Code 1.100 or later, so the environment detection path is unavailable below that.

## Sources

- [astral-sh/ruff-vscode on GitHub](https://github.com/astral-sh/ruff-vscode)
- [Issues](https://github.com/astral-sh/ruff-vscode/issues)
- [README](https://github.com/astral-sh/ruff-vscode/blob/main/README.md)
- [Releases](https://github.com/astral-sh/ruff-vscode/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/astral-sh-ruff-vscode
