
ZedでPyreflyを拡張なしで使う:有効化と型エラーを表示する設定
どうも!オペ部の西村祐二です!
Zed で Python を書くとき、型チェックや定義ジャンプを担う言語サーバーはデフォルトで basedpyright です。Meta の型チェッカー Pyrefly を使いたい場合、これまでは Pyrefly の拡張機能を入れる必要がありました。2026年10月28日リリースの Zed 1.26.2 で Pyrefly が組み込みの言語サーバーとして追加され、設定を足すだけで使えるようになりました。
ただし、pyrefly.toml などの設定ファイルがないプロジェクトでは、有効にしただけでは型の不一致が診断として返ってきません。その対処も含めて、2026年11月13日時点の最新版である Zed 1.28.1 と Pyrefly 1.4.1 で確かめた結果を、エディタの画面とあわせて整理します。
今回やること
Pyrefly は型チェッカー兼言語サーバーで、README では高速な型チェックを特徴に挙げています。コマンドラインで pyrefly check を使っているプロジェクトなら、エディタでも同じ型チェッカーの結果を見られます。
この記事で分かること:
- Zed で basedpyright の代わりに Pyrefly を使う設定
- Zed が pyrefly を探す順序(プロジェクトの
.venv、PATH、GitHub Releases) - 型エラーを表示するための
pyrefly.tomlとtypeCheckingModeの設定
Pyrefly と basedpyright の速度や検出内容の比較は扱いません。
前提条件
動作環境
- macOS 26.7.2(Apple Silicon)
- Zed 1.28.1(2026年11月11日リリース。組み込みの Pyrefly が追加された 1.26.2 より後のバージョン)
- Pyrefly 1.4.1(Zed が GitHub Releases から取得したもの)、1.3.0(プロジェクトの
.venvに入れたもの)、1.3.2(PATH 上に置いたもの)。どの pyrefly が起動したか区別できるよう、バージョンを変えています - uv 0.13.6(
.venvの作成と pyrefly の導入に使用)
確認した範囲
設定方法
やることは次の2つです。
languages.Python.language_serversに"pyrefly"を足す- 型エラーまで表示するなら、プロジェクトに
pyrefly.tomlを置くか、Zed の設定でtypeCheckingModeをdefaultにする
1. language_servers に pyrefly を追加する
Python の言語サーバーは languages.Python.language_servers で選びます。特定のプロジェクトだけで切り替えるならプロジェクト直下の .zed/settings.json に、すべてのプロジェクトで使うならユーザー設定(~/.config/zed/settings.json)に書きます。
{
"languages": {
"Python": {
"language_servers": ["pyrefly", "!basedpyright", "..."]
}
}
}
それぞれの要素の意味は次のとおりです。
| 要素 | 意味 |
|---|---|
"pyrefly" |
Pyrefly を有効にする |
"!basedpyright" |
デフォルトの basedpyright を無効にする |
"..." |
ほかにデフォルトで有効な言語サーバー(Ruff など)をそのまま使う |
Zed のドキュメントによると、Pyrefly、ty、Pyright、PyLSP はデフォルトでは無効なので "..." には含まれず、language_servers に名前を書いたときだけ有効になります。
初めて開くプロジェクトでは信頼を求めるダイアログが出て、「Trust and Continue」を選ぶまで .zed/settings.json は読み込まれません(詳しくはつまずきポイントで説明します)。
設定を書いたプロジェクトを開き、ログに出た言語サーバーを、.zed/settings.json がないプロジェクトの場合と比べました。macOS のログは ~/Library/Logs/Zed/Zed.log にあり、pyrefly で絞り込むと starting language server process. binary path: ... の行から、起動したバイナリが分かります。
| プロジェクトの設定 | 起動した言語サーバー |
|---|---|
.zed/settings.json なし |
Ruff、basedpyright |
上の .zed/settings.json |
Ruff、Pyrefly |
basedpyright が起動しなくなり、Ruff はそのまま残りました。フォーマットとリントは Ruff、型チェックや定義ジャンプは Pyrefly という分担になります。pyrefly を別途インストールしていなくても、Zed が自動で取得します。
2. Zed がどの pyrefly を起動するか
Pyrefly を有効にすると、Zed は起動する pyrefly を下の図の順に探します。図は、Zed 1.28.1 のソースコード(PyreflyLspAdapter)を読んで自作した概念図です。公式のものではありません。

- Zed が選んだ Python ツールチェーン(プロジェクトの
.venvなど)の中のpyrefly - PATH 上の
pyrefly - どちらもなければ、GitHub Releases から取得した最新版の
pyrefly(ダウンロード時に SHA-256 を照合します。保存先は Zed のデータフォルダで、macOS では~/Library/Application Support/Zed/languages/pyrefly/)
3つとも実際に動かして確かめました。用意した状態は次のとおりです。
.venvにも PATH にも pyrefly がないプロジェクトuv venvで作った.venvに、uv pip install pyrefly==1.3.0で pyrefly を入れたプロジェクトuv tool install pyrefly==1.3.2で入れた pyrefly の場所を PATH に含めて Zed を起動した場合
次のログは、Zed のログから pyrefly の主な行を抜き出したものです。日付、ログレベル、working directory を省き、パスは <data-dir> などに置き換えています。# の行は説明のために足しました。
# .venv にも PATH にも pyrefly がない場合
10:55:11 [github_releases] downloading https://github.com/meta/pyrefly/releases/download/1.4.1/pyrefly-macos-arm64.tar.gz into "<data-dir>/languages/pyrefly/pyrefly-1.4.1"
10:55:16 [lsp] starting language server process. binary path: "<data-dir>/languages/pyrefly/pyrefly-1.4.1/pyrefly", args: ["lsp"]
# .venv/bin/pyrefly がある場合
10:57:25 [language] found user-installed language server for pyrefly. path: "<project>/.venv/bin/pyrefly", arguments: ["lsp"]
10:57:25 [lsp] starting language server process. binary path: "<project>/.venv/bin/pyrefly", args: ["lsp"]
# PATH 上に pyrefly がある場合
10:57:59 [language] found user-installed language server for pyrefly. path: "<PATH 上のフォルダ>/pyrefly", arguments: ["lsp"]
10:57:59 [lsp] starting language server process. binary path: "<PATH 上のフォルダ>/pyrefly", args: ["lsp"]
.venv か PATH に pyrefly がある場合、Zed はダウンロードせずに、見つけた pyrefly をそのまま起動しています。PATH 上にある場合は、同じ pyrefly が起動直後に1回起動し直していました(抜粋では省いています)。
.venv と PATH の両方に pyrefly がある状態も1回試しました。最初に PATH 上の pyrefly(1.3.2)が起動し、直後に停止されて、代わりに .venv/bin/pyrefly(1.3.0)が起動しました。最終的に動いていたのは .venv 側です。上の 1〜3 は、最終的に使われる pyrefly の優先順位にあたります。
なお、参考リンクに挙げた PR #44262 の説明には PyPI から取得するとありますが、1.28.1 のコードと実際の動作では GitHub Releases から取得していました。
3. pyrefly.toml を置いて型エラーを表示する
次の main.py を開いて、エディタの表示と、Pyrefly が Zed に返した診断を確認しました。4行目と5行目が型の誤り、6行目が未定義の名前、7行目が存在しないモジュールの import です。
def add(a: int, b: int) -> int:
return a + b
total: int = add(1, "2")
print(total.upper())
prnt(total)
import not_installed_pkg
Pyrefly の設定ファイル(pyrefly.toml など)を置かない状態では、波線が出たのは6行目と7行目だけでした。4行目と5行目には何も出ていません。

画面の波線とステータスバーの件数には、Ruff の診断も含まれます。Pyrefly が返した診断だけを通信の記録から抜き出すと、次の3件でした。
main.py diagnostics (3):
6:1 [unknown-name] Could not find name `prnt`
7:8 [missing-import] Cannot find module `not_installed_pkg`
7:8 [unused-import] Import `not_installed_pkg` may be unused
未定義の名前(unknown-name)のほか、7行目の import については、モジュールが見つからないエラー(missing-import)と未使用のヒント(unused-import)が出ています。一方、4行目の、引数の型が合わない誤り(bad-argument-type)と、5行目の、存在しない属性の参照(missing-attribute)は出ませんでした。
ステータスバーのエラー3件は、Pyrefly の unknown-name と missing-import に Ruff の F821(未定義の名前)を足したもので、警告1件は Ruff の F401(未使用の import)です。Pyrefly の unused-import はヒントとして返っていて、この件数には入っていません。
この動きは、Pyrefly のドキュメントにある typeCheckingMode の説明と一致します。デフォルトの auto では、Pyrefly の設定ファイルがなければ近くにある mypy や Pyright の設定を読み込んで使い、それも見つからなければ確度の高いエラーだけを出す basic プリセットで動きます。今回のサンプルには mypy や Pyright の設定もないため、basic の範囲の3件だけになったと考えられます。
プロジェクト直下に pyrefly.toml を置くと、4行目と5行目にも波線が出ました。中身は Zed のドキュメントにある最小の例です。
# Check all Python files in your project
project-includes = ["."]

上の画面は、4行目の "2" にマウスを重ねたところです。Pyrefly の bad-argument-type がポップアップに出ています。Pyrefly が返した診断は、bad-argument-type と missing-attribute が加わって5件になっていました。ステータスバーのエラー5件は、Pyrefly のエラー4件に Ruff の F821 を足したものです。
Project Diagnostics パネル(cmd-shift-m)を開くと、診断ごとに Pyrefly と Ruff のどちらが出したものか、ルール名とあわせて確認できます。

Pyrefly のドキュメントでは [tool.pyrefly] を書いた pyproject.toml も設定ファイルとして扱うとされています。こちらは Zed を介さず、pyrefly 1.4.1 に LSP で直接つないで試し、pyrefly.toml と同じく bad-argument-type と missing-attribute が返ることを確認しました。
4. 設定ファイルを置かずに型エラーを表示する
リポジトリに pyrefly.toml を足せない場合や、個人の Zed 設定だけで済ませたい場合は、ユーザー設定の lsp で typeCheckingMode を default にします。
Pyrefly 1.4.1 のドキュメントが説明しているのは、LSP の initializationOptions で設定を送る方法です。VS Code 以外のエディタから送るキーは、VS Code の設定名から先頭の python. を外したもの(pyrefly.typeCheckingMode)とされています。Zed では lsp.pyrefly.initialization_options がこれにあたります。
{
"lsp": {
"pyrefly": {
"initialization_options": {
"pyrefly": { "typeCheckingMode": "default" }
}
}
}
}
この設定で開くと、Pyrefly の診断は pyrefly.toml を置いたときと同じ5件になり、エディタでも4行目と5行目に波線が出ました(画面は、つまずきポイントに載せた比較画像の下側です)。
initialization_options の代わりに settings に書いても、同じ結果でした。その場合は、次のように python の下に書きます。Pyrefly は python という名前のセクションの設定を Zed に問い合わせ、Zed は settings 内の python の値を返すためです。書くのはどちらか一方で足ります。
{
"lsp": {
"pyrefly": {
"settings": {
"python": {
"pyrefly": { "typeCheckingMode": "default" }
}
}
}
}
}
Pyrefly のドキュメントによると、pyrefly.toml や pyproject.toml の [tool.pyrefly] が適用されるファイルでは、typeCheckingMode よりもそちらの設定が優先されます。pyrefly.toml を置いたプロジェクトに、すべてのエラーを止める typeCheckingMode: "off" を送っても、bad-argument-type と missing-attribute は返ってきました。これは pyrefly 1.4.1 に LSP で直接つないで確かめた結果で、Zed の設定経由では試していません。
設定のポイント
-
リポジトリに設定を残せるなら pyrefly.toml を置く
- Zed のドキュメントも設定ファイルを推奨しています。
pyrefly checkを手元や CI で実行したときも、同じpyrefly.tomlが読まれます(バージョンのそろえ方はポイント 3 で説明します)。mypy や Pyright の設定がある場合は、pyrefly initで移行できると README に書かれています
- Zed のドキュメントも設定ファイルを推奨しています。
-
個人の設定だけで済ませるなら typeCheckingMode を使う
- リポジトリに手を入れずに済みますが、型エラーが出るのは設定を書いた自分の Zed だけです。
pyrefly.tomlがあるプロジェクトでは、設定方法 4 で書いたとおり設定ファイル側が優先されます
- リポジトリに手を入れずに済みますが、型エラーが出るのは設定を書いた自分の Zed だけです。
-
Pyrefly のバージョンを固定したいならプロジェクトの .venv に入れる
- Zed が GitHub Releases から取得するのは、その時点の最新版です。2026年10月31日に試したときは 1.4.0 を、11月13日は 1.4.1 を取得しました。Pyrefly の README には、マイナーバージョンが毎月出ること、どのバージョンでも新しい型エラーが出る可能性があることが書かれています。バージョンを固定した pyrefly を
.venvに入れておくと、Zed もそのバージョンを使うので、CI のpyrefly checkと結果を合わせやすくなります
- Zed が GitHub Releases から取得するのは、その時点の最新版です。2026年10月31日に試したときは 1.4.0 を、11月13日は 1.4.1 を取得しました。Pyrefly の README には、マイナーバージョンが毎月出ること、どのバージョンでも新しい型エラーが出る可能性があることが書かれています。バージョンを固定した pyrefly を
つまずきポイント
プロジェクトを信頼するまで言語サーバーが起動しない
Zed のドキュメントでは、初めて開くプロジェクトをユーザーが信頼するまでの状態を Restricted Mode と呼び、その間は .zed/settings.json の読み込みと、プロジェクトの言語サーバーの導入・起動を止めると説明しています。
信頼していない状態で、.zed/settings.json に Pyrefly を設定したプロジェクトを開くと、次のダイアログが出ました。

このときのログには Waiting for worktree ... to be trusted, before starting language server basedpyright と出ていました。Pyrefly ではなく basedpyright の起動を待っているので、プロジェクトの設定がまだ読み込まれていないことが分かります。「Trust and Continue」を選ぶと、プロジェクトの設定が読み込まれ、Pyrefly と Ruff が起動しました。
ドキュメントによると、あとから信頼する場合は、タイトルバーの感嘆符のアイコン(1.28.1 では「Restricted Mode」と表示されています)か workspace::ToggleWorktreeSecurity アクションでこのダイアログを開けます。
すべてのプロジェクトを信頼済みとして扱う session.trust_all_worktrees という設定もありますが、ドキュメントでは推奨されていません。有効にした状態で開くと、確認なしで .zed/settings.json が読み込まれ、.venv/bin/pyrefly のようなプロジェクト内のバイナリが起動しました。
Zed のドキュメントにある displayTypeErrors の例では型エラーが返らなかった
Zed のドキュメントには、設定ファイルを置かずに型エラーを表示する方法として、lsp.pyrefly.initialization_options に {"python": {"pyrefly": {"displayTypeErrors": "force-on"}}} を書く例があります(2026年11月13日時点の main ブランチでも同じ記述です)。同じ日に、Zed 1.28.1 と Pyrefly 1.4.1 の組み合わせで、この例をユーザー設定に書いて試しました。Zed はこの値をそのまま Pyrefly に送っていましたが、Pyrefly の診断は設定ファイルを置かないときと同じ3件のままで、4行目と5行目に波線は出ませんでした。

上が Zed のドキュメントの例、下が {"pyrefly": {"typeCheckingMode": "default"}} を initialization_options に書いた場合です。どちらも pyrefly.toml は置いていません。
切り分けとして、pyrefly 1.4.1 に LSP で直接つないだ場合も試しました。python の下に置いた {"python": {"pyrefly": {"displayTypeErrors": "force-on"}}} では型エラーが返らず、python を外した {"pyrefly": {"displayTypeErrors": "force-on"}} では返りました。設定方法 4 のとおり、initialization_options で Pyrefly 1.4.1 が読むキーは python. を外した位置にあります。Zed のドキュメントの例はキーを python の下に置いているため、読まれていないと考えられます。
また、Pyrefly 1.4.1 のドキュメントでは、displayTypeErrors が非推奨になり、typeCheckingMode と disableTypeErrors に置き換えられています(force-on は typeCheckingMode = "default" に相当します)。Zed のドキュメントの例が python. を外した書き方に変われば、この項は当てはまらなくなります。
GitHub API のレート制限に当たると初回の取得に失敗する
取得済みの pyrefly がない状態で GitHub API のレート制限に当たると、ログに Failed to start language server "pyrefly": status error 403 と API rate limit exceeded が出て、Pyrefly が起動しません。Zed は GitHub Releases の最新バージョンを GitHub API に問い合わせていて、未認証のリクエストは IP アドレスごとに1時間60回までに制限されているためです。
取得済みの pyrefly がデータフォルダにある場合は、同じ 403 が返っても、ログに failed to fetch newest version of language server と出たうえで取得済みのバイナリが起動しました。制限に当たっている間も、.venv に入れた pyrefly は起動しました。
今回は、出口の IP アドレスをほかの利用者と共有する VPN 経由で、空のデータフォルダでの起動を繰り返していたときに発生しました。普段の使い方でどの程度起きるかは確かめていません。PATH だけに pyrefly を置いた場合も、制限中には試していません。
まとめ
Zed 1.26.2 以降では、languages.Python.language_servers に "pyrefly" を足すだけで、拡張機能を入れずに Pyrefly を使えます。basedpyright を止めるなら "!basedpyright" を、Ruff を残すなら "..." を一緒に書きます。
Zed は、起動する pyrefly をプロジェクトの .venv、PATH の順に探し、どちらにもなければ GitHub Releases の最新版を取得します。コマンドラインや CI とバージョンをそろえたい場合は、.venv に入れておくと合わせやすくなります。
型の不一致まで表示するには、プロジェクトに pyrefly.toml を置くか、Zed の lsp 設定で typeCheckingMode を default にします。Zed のドキュメントにある displayTypeErrors の例を書いても、Zed 1.28.1 と Pyrefly 1.4.1 の組み合わせでは診断が変わりませんでした。設定後は、この記事と同じ main.py の4行目と5行目に波線が出るか、Project Diagnostics パネルに bad-argument-type と missing-attribute が並ぶかで確認できます。
誰かの参考になれば幸いです。
参考リンク:






