miya8060
← back to blog

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 を回す。

neovimdppdenopstroubleshooting

: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 DenopsReadydpp.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 後に同じ目に遭ったときに自分で気付ける。