NOTE · 2026-05-09
dpp.vim の DppMakestate が "Not found ext" で落ちたときの復旧
`:DppUpdate` のあと `:DppMakestate` が `Not found ext: "toml"` で落ちて plugin が読まれなくなる事象。ext のディレクトリ構造移行 + キャッシュ symlink 破損 + denops のランタイム要件不足の合成で起きるので、state.vim 削除だけでは直らない。`.dpp` ごと消して、ランタイムを上げてから headless で makestate を回す。
:DppUpdate で plugin を更新した直後、:DppMakestate が以下のエラーで落ちて state.vim / startup.vim が再生成されなくなった。
[dpp] Not found ext: "toml"
[denops] Failed to load file '~/.config/nvim/dpp.ts':
TypeError: Cannot read properties of undefined (reading 'plugins')
at Config.config (~/.config/nvim/dpp.ts:77:23)
state が再生成されないので、次回 nvim 起動時に plugin が一切読まれない。dotfiles の既存ノートに沿って state.vim を消して再起動しても直らず、.dpp ごと消してランタイムを上げるまで戻らなかった。原因が 3 層構造になっていたので残しておく。
原因の三層構造
この症状は 3 つの問題の合成 で起きる。1 つでも欠けると典型症状にならない。
1. dpp ext 群の構造移行
Shougo/dpp-ext-{toml,lazy,installer} がいずれも import-map 利用のため、denops/@dpp-exts/<name>.ts 単体ファイルから denops/@dpp-exts/<name>/main.ts + deno.json のディレクトリ構造に再構成された (2025-08-30 頃)。dpp.vim 側の loader は *.ts と */main.ts の両 glob を見るので、新構造でも検出できる はず ではある。
2. ~/.cache/dpp/nvim/.dpp/ の壊れた symlink
dpp は makestate 時に ~/.cache/dpp/nvim/.dpp/denops/@dpp-exts/ 配下に「アクティブな ext を集約した symlink ツリー」を生成する。前回 (= 旧構造時) の makestate が作った symlink は旧パス <repo>/denops/@dpp-exts/toml.ts を指したまま残る。:DppUpdate で実体が toml/main.ts に移動したので symlink が壊れる。
globpath は壊れた symlink をスキップするが、denops の plugin 解決系は .dpp 配下を巡回するので影響が出る。@dpp-exts/toml 名で ext を解決しに行くと、新側 (repo 直下の toml/main.ts) と壊れた側 (.dpp/.../toml.ts 不在) の両方が候補に上がり、解決失敗となる。
3. nvim / deno のランタイム要件不足
新しい denops は denops/supported_versions.json で要件を上げている。
{ "deno": "2.3.0", "vim": "9.1.1646", "neovim": "0.11.3" }
要件未満だと denops は起動時に warning を出すが 継続実行する。継続するが ext loader 系の挙動が劣化し、上記 (2) の壊れ symlink を黙殺できなくなる。これが結果として「Not found ext: toml」を Vim 側に返す。
つまり (1) の構造移行が引き金、(2) の stale symlink が地雷、(3) のバージョン不足が地雷を踏んで爆発させる役回り。
診断手順
順番に当てて、どこまで成立しているかを切り分ける。
a. dpp 関連の git pull が走ったかを確認
for d in ~/.cache/dpp/repos/github.com/*/*/.git; do
repo=$(dirname "$d")
printf "%s/%s %s\n" "$(basename $(dirname $repo))" "$(basename $repo)" \
"$(git -C "$repo" rev-parse --short HEAD)"
done | sort
直近の :DppUpdate の前後で diff を取ると、dpp-ext-toml / dpp-ext-lazy / dpp-ext-installer / dpp.vim / denops.vim あたりが動いていれば容疑者。
b. 新構造に移行済みかを確認
ls ~/.cache/dpp/repos/github.com/Shougo/dpp-ext-toml/denops/@dpp-exts/
# toml.ts (旧) ではなく toml/ ディレクトリだけがあれば移行済み
ls ~/.cache/dpp/repos/github.com/Shougo/dpp-ext-toml/denops/@dpp-exts/toml/
# main.ts と deno.json があれば新構造
c. 壊れた symlink を一覧
for f in ~/.cache/dpp/nvim/.dpp/denops/@dpp-exts/*.ts; do
target=$(readlink "$f")
[ -e "$target" ] && s="OK " || s="BROKEN"
printf " %s %s -> %s\n" "$s" "$(basename $f)" "$target"
done
BROKEN が 1 本でも出れば (2) が成立している。
d. denops のバージョン要件と現状を突き合わせ
cat ~/.cache/dpp/repos/github.com/vim-denops/denops.vim/denops/supported_versions.json
nvim --version | head -1
deno --version | head -1
要件未満なら (3) が成立。
復旧手順
3 ステップを この順 で実行する。順序を入れ替えるとどこかで再発する。
1. ランタイムを要件まで上げる
macOS なら:
brew upgrade neovim deno
nvim --version | head -1 # >= 0.11.3
deno --version | head -1 # >= 2.3.0
これを飛ばすと次のステップでも同じ症状が再発する。
2. dpp の派生キャッシュを全削除
.dpp の symlink ツリーと state は makestate で再生成されるので、消して問題ない。repos/ (= 実体) は触らない。
rm -rf ~/.cache/dpp/nvim/.dpp \
~/.cache/dpp/nvim/state.vim \
~/.cache/dpp/nvim/startup.vim
3. headless で makestate を完了させる
:DppMakestate は denops 経由の非同期処理なので、User Dpp:makeStatePost event で qa! する autocmd を組んで待つ。タイマーは保険。
nvim --headless \
-c 'autocmd User Dpp:makeStatePost qa!' \
-c 'call timer_start(180000, {-> execute("qa!")})'
init.lua 起動時に dpp.load_state() が失敗 → User DenopsReady で dpp.make_state() が走る → 完了で qa!。.dpp/denops/@dpp-exts/ 配下が installer/ lazy/ toml/ の ディレクトリ に再生成されていれば成功。
検証
nvim --headless +'echo "plugin count: " . len(keys(dpp#get()))' +qa
数字が返ればロード自体は成功している。エラー出力がなければ起動も clean。
なぜ「state.vim 削除で再起動」だけでは直らないか
dotfiles 側の既存トラブルシュートは state.vim / startup.vim の削除しか書いていなかった。それだけだと .dpp/denops/@dpp-exts/ の壊れ symlink が残るので、makestate が再失敗する。.dpp ディレクトリごと削除する が今回の追加知見。
加えて、ext 構造移行 (*.ts → */main.ts) のタイミングで denops のランタイム要件も同時に上がっていたので、キャッシュを消すだけでは復旧せず、brew upgrade neovim deno まで含めて 3 点セットになる。
再発予防
- denops のバージョン要件は
supported_versions.jsonがソースオブトゥルース。dpp ecosystem を更新する前に一度確認しておくと、ランタイム側を先に上げる判断ができる。 .dpp/配下の symlink は makestate のたびに作り直される という前提で扱う。手動で symlink を直すよりキャッシュ全削除 → makestate のほうが確実。- dotfiles のトラブルシュートに「
Not found ext系は.dppの壊れ symlink + ランタイム要件不足の合成」と明記しておくと、次回の:DppUpdate後に同じ目に遭ったときに自分で気付ける。