0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cursor で Dev Container 上での ln -s が原因で Dev Container が起動できなくなる話

0
Last updated at Posted at 2026-03-20
(2026.4.5追記)
本記事で説明されている不具合は、Cursor 2.6.22以降(ver. 3.x含む)で解消済みのようです。
(2026.4.8追記)
再度不具合が再現しました。最新版でも不安定な状態です。
(2026.5.1追記)
Cursor 3.2.11で再度不具合が解消した模様です。

リポジトリを Windows の NTFS ドライブ に clone し、Dev Container で開発しているとき、コンテナ内のスクリプトで Git 管理下のシンボリックリンクを ln -s で復元すると、ホスト側で Cursor の拡張ホストが連続クラッシュしたり、Git がファイルを開けないことがあります。

本稿では、実際に観測した事象と切り分け、原因が NTFS 上の LX_SYMLINK(再解析ポイント)であること、確認コマンド、NTFS に clone したまま運用する場合のワークアラウンドを整理します。本事象についてはCursorサポートに報告し改善依頼済みです。

想定読者: Windows(NTFS)に clone したリポジトリを Dev Containers で開き、Cursor で開発している人(特に Extension host terminated unexpectedly に悩んでいる人)

まず確認(最短ルート)

次に当てはまる場合、NTFS 上の「再解析ポイント(LX_SYMLINK 系)」が原因になっている可能性があります。

  • Cursor で Extension host terminated unexpectedly 3 times within the last 5 minutes. が出る
  • Windows 上でリポジトリを NTFS に clone し、Dev Container で bind mount して使っている
  • 対象ファイルに対して fsutil reparsepoint query を実行すると、0xa000001d(LX_SYMLINK 系)に相当するタグが見える

現象

Cursor

次のようなメッセージが出たり、Dev Container の起動やウィンドウ操作が不安定になることがあります。

  • Extension host terminated unexpectedly 3 times within the last 5 minutes.

%APPDATA%\Cursor\logs\<日付>\main.log には、例えば次のような行が並ぶことがあります。

[error] [... type: extensionHost ...]: crashed with code -1073740791 and reason 'crashed'

-1073740791 は 32 ビット符号付き整数として解釈すると、多くの環境で 0xC0000409(STATUS_STACK_BUFFER_OVERRUN) に相当します。ネイティブ側の想定外のパスやファイル種別に触れたときに現れうるクラッシュコードの一つです(環境・バージョンにより異なる場合があります)。

Git(Git for Windows など)

ワークツリー上の特定パスに対して、次のようなエラーが出ることがあります。

error: open("scripts/example.sh"): Function not implemented
fatal: cannot hash scripts/example.sh

git diff やインデックスのハッシュ計算など、ファイルを普通のファイルとして開いて読む処理で失敗するパターンです。


環境・前提(再現しやすい構成)

次をすべて満たすと、本稿の事象が起きやすいです。

項目 内容
リポジトリの場所 C:\ や D:\ など NTFS 上 に clone している。
Dev Container 上記フォルダを バインドマウント してコンテナ内の /workspaces/... として使っている。
シンボリックリンクの作成(ln -s) 後述の 「なぜコンテナ内で ln -s するのか」 に該当する運用。postCreateCommand や起動スクリプトなどから ln -s を実行し、ワークツリー上の該当パスを Linux の実シンボリックリンクにしている。
リンク先 同一リポジトリ内だけでなく、../../別リポジトリ/scripts/... のように 隣接 clone したリポジトリ を指す場合もある。

Windows 上で エディタでファイルを編集しない(常にコンテナ内だけで編集する)場合でも、Cursor の本体や一部プロセスは Windows 側で動き、同じ NTFS パス上のファイルに触れるため、ホスト側の問題として表面化し得ます。


試しても症状が変わらなかったこと(参考)

次は「キャッシュや設定の汚れ」を疑って試しやすい対策ですが、本稿の原因がディスク上のシンボリックリンクそのものである場合、根本解決にはならないことがあります。

  • ワークスペース直下の .cursor など、エディタ用データの削除
  • .cache の削除
  • .devcontainer の削除(※ コンテナ定義が消えるだけで、既に作られたリンクは残る)
  • ワークスペースフォルダのリネーム
  • %APPDATA%\Cursor\User\workspaceStorage\ の削除
  • code --disable-extensions / Cursor 相当の 拡張機能無効での起動

拡張機能を切っても、組み込みの Git・ファイル監視・インデックスなどが同じパスに触れるため、再現しうる、という整理になります。


補足:なぜコンテナ内で ln -s することがあるのか

Git には シンボリックリンクとして履歴に載せるエントリ(インデックス上は symlink 扱い)があります。Windows で clone したとき、(特に core.symlinks false などの条件下で)管理者権限や設定の都合により「本物のシンボリックリンク」として checkout されず、リンク先パス文字列が入った小さな通常ファイルとして checkout されることがあります。

その状態だと、コンテナ内のシェルやツールは 「これは symlink ではない」 とみなすため、パス解決や実行が意図どおりになりません。そこで当初は次のような対策を入れていました。いまから思えばこれが諸悪の根源でした。

  1. コンテナが立ち上がったタイミング(例: postCreateCommand、エントリポイント、手動スクリプト)で、Git が symlink として知っているパスを列挙する。
  2. 該当パスをいったん消し、ln -s <リンク先> <そのパス> で Linux 上の実シンボリックリンクとして作り直す。

コンテナの中だけを見れば合理的なのですが、ワークスペースが ホストの NTFS フォルダのバインドマウントである場合、ln -s は ホスト側ディスク上の同じ場所にリンクを書き込みます。結果として Windows からも見える特殊なファイル(再解析ポイント) が残り、本稿の問題につながります。

いけなかったは 「Git の symlink をコンテナ起動時に ln -s で実体化した」 事だと理解してください。


原因の整理:NTFS に現れる「Linux 向けシンボリックリンク」

何が起きているか

  1. clone 直後(Windows / core.symlinks false が有効になっていることが多い)
    Git はシンボリックリンクエントリを、リンク先パス文字列が入った小さな通常ファイルとして checkout することがあります。NTFS 上は 普通のファイル に見えます。

  2. Dev Container 内で、シンボリックリンクとして ln -s が実行される
    バインドマウントされた 同じ NTFS 上のパス に、Linux から 実シンボリックリンク が作成されます。

  3. Windows から見ると
    そのエントリは 再解析ポイント(reparse point) として扱われ、多くの場合 WSL / Linux 互換のシンボリックリンク(LX_SYMLINK) に相当するタグ(後述)で記録されます。

  4. Git for Windows や Cursor 側のネイティブ処理が、そのパスを 通常ファイルとして open / 読み取りしようとすると、**未対応(Function not implemented)**になったり、クラッシュにつながることがあります。

要点は、「コンテナの中では正しい Linux の symlink」でも、ホスト NTFS には別物として残ることです。Dev Container 用の設定フォルダ(例: .devcontainer)を消しても、ワークツリー上にすでに作られたリンクは消えません。


LX_SYMLINK っぽいかどうかの確認コマンド

fsutil(タグまで確かめたいとき・推奨)

管理者権限の要否は環境によります。まずは通常ユーザーで試してかまいません。

fsutil reparsepoint query "D:\path\to\repo\scripts\example.sh"

出力の 再解析タグが 0xa000001d の場合、多くの環境で LX_SYMLINK(Linux 互換シンボリックリンク) を示します。

参考までに、他のよくあるタグの例です。

タグ(例) 意味の一例
0xa000001d LX_SYMLINK(本稿で問題になりやすい)
0xa000000c NTFS シンボリックリンク
0xa0000003 ジャンクション

ターゲットパスは、出力の 16 進ダンプに ASCII としても現れることがあります。

PowerShell(属性のざっくり確認)

Get-Item "D:\path\to\repo\scripts\example.sh" -Force |
  Select-Object FullName, Attributes, LinkType, Target

Attributes に ReparsePoint が含まれる場合、再解析ポイントです。LinkType / Target が空のままのこともあるため、確定は fsutil reparsepoint query が確実です。

cmd の dir

dir "D:\path\to\repo\scripts\example.sh"

<SYMLINK> / <JUNCTION> などと表示されることもありますが、種類によっては紛らわしいため、やはり fsutil を推奨します。


対処策

1. リポジトリを WSL2 の Linux ファイルシステムに置く

**「NTFS 上に Linux 実体の symlink を残さない」**のが最も筋が良いです。

  • WSL のホームとは、Linux ディストリビューション内の /home/<ユーザー名>(Windows の C:\Users\... とは別物)です。
  • リポジトリを /home/... 配下(ext4 側) に clone し、Docker / Cursor もそのパス(例: \\wsl$\DistroName\home\UserName\src\RepoName ― 各セグメントは環境に合わせて読み替え)を開く運用にすると、同じ ln -s でも NTFS に LX_SYMLINK が生えにくい(ホストがそのツリーを直接 NTFS として解釈しない)構成に寄せられます。

詳細は WSL / Docker Desktop の公式ドキュメントも参照してください。

2. NTFS に clone したまま運用する場合のワークアラウンド

要件: Windows ドライブに clone する前提を外せない。一方でコンテナ内では パス・挙動を symlink 相当で保ちたい。

方針 内容
Git(Windows) core.symlinks false が有効な場合、ホスト上は 一般に実シンボリックリンクを作らず、symlink エントリが リンク先パス文字列が入った小さな通常ファイルとして checkout されることがあります。
コンテナ(mounts) ln -s で NTFS に書き込まないようにする。代わりに Dev Containers の mounts で、リンク先の実ファイルを scripts/... などのパスにファイル単位でバインドし、コンテナ内からは同じパスで読めるようにする。
注意(mounts) symlink 1 本につきマウントが増えるため、devcontainer.json のメンテが必要。既に隣接リポジトリ全体を /workspaces/... にマウントしている場合でも、scripts/foo というパスで見せたいなら、ホストパス同士の ファイル単位 bind が有効なことが多いです。

問題を起こしうるパターンと、mounts 方式への置き換え

名前はリポジトリごとに違いますが、次の対比で整理すると分かりやすいです。

例 NTFS バインド上で起きやすいこと 本件での扱い
ln -s で symlink を作り直す ホスト NTFS に LX_SYMLINK 等が残り、本稿の Cursor / Git 障害につながりうる 回避
mounts で必要パスをファイル単位 bind ホストに再解析ポイントを増やさず、コンテナ内では必要パスを維持しやすい 推奨

mounts 方式は、**「NTFS に再解析ポイントを増やさず、Git 差分運用にも追加制約を持ち込みにくい」**のが利点です。以下のスクリプト例は、比較・経緯説明のために残しています(本文の手順としては扱いません)。

問題パターンの例(ln -s で symlink を作り直す)

以下のスクリプトは実際に運用して不具合の元となった例。Git が symlink として記録しているパスに対し ln -s で実シンボリックリンクを作成する。末尾の .cursor 配下の設定ファイルを、隣接マウントした別リポジトリへ向けるブロックも同様に ln -s である。

#!/bin/bash
# 本スクリプトは不具合の発生原因例として掲載。NTFSマウントしている
# リポジトリでは使わないようにして下さい。

# Windows環境で一般権限cloneしたリポジトリでは、Gitがシンボリックリンクを
# 通常ファイル(リンク先パスが内容)としてcheckoutするため、Dev Container
# 起動時にコンテナ内で正しいシンボリックリンクへ復元する。
set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT"

if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    echo "restore_symlinks:  Gitリポジトリ外のためスキップします"
    exit 0
fi

count=0
# Gitの120000(symlink)エントリを取得。パスはタブの後に続く
while IFS= read -r line; do
    mode="${line%% *}"
    [ "$mode" != "120000" ] && continue
    # 形式: "120000 sha1 stage<TAB>path" のため、最後のタブの後がパス
    path="${line##*	}"
    [ -z "$path" ] && continue

    target=""
    target=$(git show ":$path" 2>/dev/null) || true
    [ -z "$target" ] && continue
    # Windows由来のCRLFを除去
    target="${target%"${target##*[![:space:]]}"}"
    target="${target//$'\r'/}"
    # シンボリックリンクのターゲットは1行のパスのみ。改行を含む場合はindex不整合のためスキップ
    if [ "${target#*$'\n'}" != "$target" ] || [ "${#target}" -gt 1024 ]; then
        echo "restore_symlinks:  スキップ(不正なターゲット): $path"
        continue
    fi

    if [ -L "$path" ]; then
        current=$(readlink "$path")
        [ "$current" = "$target" ] && continue
    fi

    rm -f "$path"
    ln -s "$target" "$path"
    echo "restore_symlinks:  復元 $path -> $target"
    count=$((count + 1))
done < <(git ls-files -s)

if [ "$count" -eq 0 ]; then
    echo "restore_symlinks:  復元対象のシンボリックリンクはありません(既に正しいか、120000がありません)"
else
    echo "restore_symlinks:  $count 件のシンボリックリンクを復元しました"
fi

# 例: ワークスペースの .cursor/mcp.json を、隣接 clone した別リポジトリ内のファイルへの symlink として復元する
# (設定は別リポジトリで管理し、本リポジトリではリンクのみ作成するパターン)
# 隣接リポジトリ名は次の1行だけ変えれば、マウントパス・相対パスともに追従する
SIBLING_REPO_NAME="sibling-repo"
SIBLING_REPO_ROOT="/workspaces/${SIBLING_REPO_NAME}"
LINK_PATH="$REPO_ROOT/.cursor/mcp.json"
TARGET_REL="../../${SIBLING_REPO_NAME}/.cursor/mcp.json"
TARGET_ABS="${SIBLING_REPO_ROOT}/.cursor/mcp.json"

if [ ! -d "$SIBLING_REPO_ROOT" ]; then
    echo "restore_symlinks:  MCP用リンクはスキップ(隣接リポジトリ未マウント)"
else
    if [ -f "$TARGET_ABS" ]; then
        mkdir -p "$(dirname "$LINK_PATH")"
        need_link=1
        if [ -L "$LINK_PATH" ]; then
            current_norm=$(readlink -f "$LINK_PATH" 2>/dev/null || true)
            target_norm=$(readlink -f "$TARGET_ABS" 2>/dev/null || true)
            if [ -n "$current_norm" ] && [ "$current_norm" = "$target_norm" ]; then
                need_link=0
            fi
        fi
        if [ "$need_link" -eq 1 ]; then
            rm -f "$LINK_PATH"
            ln -s "$TARGET_REL" "$LINK_PATH"
            echo "restore_symlinks:  MCP用リンクを復元しました .cursor/mcp.json -> $TARGET_REL"
        fi
    else
        echo "restore_symlinks:  MCP用リンク先が存在しないためスキップ $TARGET_ABS"
    fi
fi

4. あまり推奨しない案:コンテナ停止時だけリンクを解く

コンテナ終了時に symlink を消して Windows 向けの状態に戻す、という発想はありますが、

  • 異常終了すると リンクが NTFS に残る
  • Dev Containers の ライフサイクル(postStop 等)の実行タイミング・場所は製品・設定で解釈がぶれうる

ため、運用上の再現性が低いです。可能なら、WSL2 の Linux ファイルシステム上に clone して開くか、NTFS のままなら ln -s に頼らず core.symlinks false と Dev Container の mounts(bind mount)でパスを満たす方を優先するのがよいです。


まとめ

項目 内容
起きうること NTFS 上のワークツリーに対し、コンテナ内 ln -s が LX_SYMLINK 等の再解析ポイントを作る → Windows 上の Cursor / Git が失敗・クラッシュ。
切り分け エディタ用データの削除だけでは直らないことがある。fsutil reparsepoint query でタグを確認する。
即効 問題の再解析ポイント(Linux 実体の symlink 側)を削除し、ワークツリーを 通常ファイル状態に戻す。
当面のワークアラウンド NTFS 上では ln -s によるシンボリックリンクの作成をしない(core.symlinks false + mounts(bind mount)で必要パスを満たす)。または WSL2 の Linux FS 上に cloneする。

参考リンク

※ 本稿は特定バージョンの Cursor / Git for Windows / Docker Desktop に依存する挙動を含むため、ログの文言や終了コードは 環境により異なる場合があります。再解析タグの意味も、OS バージョンで読み取り方が変わることがあります。


本記事検証時のCursorバージョン情報ほか

Version: 2.6.20 (user setup)
VSCode Version: 1.105.1
Commit: b29eb4ee5f9f6d1cb2afbc09070198d3ea6ad760
Date: 2026-03-17T01:50:02.404Z
Build Type: Stable
Release Track: Default
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Windows_NT x64 10.0.26200

git version 2.47.3

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?