Husky v9 で pre-commit フックが動かない問題
チームに lint や型チェックを強制するために Husky + lint-staged を導入したのに、「コミット時にフックが実行されない」「lint-staged が動かない」「CI で毎回フックエラーが出る」といった問題に悩まされるケースは多いです。
特に Husky v8 → v9 への移行や、新規メンバーがクローン後に環境構築したタイミングで問題が起きやすい。
この記事では、よくある5つの原因とそれぞれの対処法を具体的なコードとエラーメッセージとともに解説します。
よくある症状
git commitしてもフックが実行されずそのままコミットが通るhusky - pre-commit hook exited with code 127 (command not found)が出るlint-stagedを実行したコマンドが見つからないと言われる- CI(GitHub Actions)でフックエラーが発生してビルドが落ちる
npm install後にフックが設定されていない
原因1: v8 スタイルのフックファイルが v9 で動かない
最もよくある原因です。Husky v9 では内部構造が大きく変わり、v8 で必須だった husky.sh のソース読み込みが廃止されました。
v8 スタイルのフックファイル(動かない):
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
v9 では .husky/_/husky.sh ファイル自体が存在しないため、. "$(dirname -- "$0")/_/husky.sh" の行で以下のエラーが出てフックが失敗します。
.husky/pre-commit: 2: .husky/pre-commit: ../_/husky.sh: not found
husky - pre-commit script failed (code 127)
v9 スタイルのフックファイル(正しい書き方):
npx lint-staged
シバン(#!/usr/bin/env sh)も不要です。v9 のフックファイルはシンプルに「実行したいコマンドだけ」書けばOKです。
移行コマンド:
# v8 → v9 移行時に自動で書き換えてくれるコマンド
npx husky init
ただし husky init は既存のフックファイルを上書きするため、カスタム内容がある場合は事前にバックアップを取っておきましょう。
原因2: フックファイルのパーミッションが足りない
macOS・Linux 環境では、.husky/ 以下のスクリプトに実行権限(+x)が付いていないと動きません。
症状:
hint: The '.husky/pre-commit' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.
対処法:
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg # commit-msg フックを使う場合も同様
Git にパーミッション変更を記録するには:
git update-index --chmod=+x .husky/pre-commit
git add .husky/pre-commit
git commit -m "chore: fix husky hook permissions"
これをコミットしておけば、他のメンバーがクローン後にパーミッション問題で詰まることを防げます。
原因3: lint-staged のコマンドが見つからない(PATH 問題)
husky - pre-commit hook exited with code 127 (command not found)
code 127 は「コマンドが見つからない」を意味します。原因は以下のいずれかです。
原因3-a: npx が使えない
フックファイルで lint-staged とだけ書いていると、グローバルにインストールされていない場合に失敗します。
# 悪い例
lint-staged
# 良い例(npx を明示)
npx lint-staged
原因3-b: Node.js が PATH に入っていない(macOS + GUI アプリ)
Sourcetree や GitHub Desktop などの GUI Git クライアントからコミットすると、シェルの PATH が通っていないため node や npx が見つからないことがあります。
対処法として、フックファイルの先頭で PATH を明示します:
# macOS で homebrew の Node.js を使う場合
export PATH="/opt/homebrew/bin:$PATH"
npx lint-staged
または .huskyrc や package.json の husky セクションではなく、.nvmrc や .node-version と合わせて以下のように書く方法もあります:
# nvm を使う場合
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
npx lint-staged
原因4: CI 環境でフックが実行されてビルドが落ちる
GitHub Actions などの CI 環境では、npm install の prepare スクリプトで Husky がインストールされ、その後のコミット操作やテストでフックが実行されることがあります。
CI でフックは不要なので、HUSKY=0 環境変数でスキップできます。
GitHub Actions での設定例:
# .github/workflows/ci.yml
jobs:
test:
runs-on: ubuntu-latest
env:
HUSKY: 0 # Husky フックを無効化
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
または npm ci 時に prepare スクリプトをスキップする方法:
- run: npm ci --ignore-scripts
- run: npm test
ただし --ignore-scripts を使うとプロジェクトの他の postinstall 等も動かなくなるため、HUSKY=0 の方が副作用が少なくおすすめです。
原因5: prepare スクリプトが設定されておらず新規クローン後にフックが入らない
新メンバーがリポジトリをクローンして npm install したのに、コミット時にフックが実行されない場合、package.json の prepare スクリプトが設定されていない可能性があります。
確認ポイント:
{
"scripts": {
"prepare": "husky"
}
}
この prepare スクリプトがあることで、npm install 実行後に自動的に Husky の初期化(.git/hooks/ へのシンボリックリンク設定)が行われます。
# prepare スクリプトを追加してから再インストール
npm pkg set scripts.prepare="husky"
npm install # これで husky の初期化が走る
よくある誤解: .husky/ ディレクトリをコミットしてあれば動く、と思いがちですが、.git/hooks/ にシンボリックリンクが貼られていないと Git はフックを実行しません。prepare スクリプトがその橋渡しをします。
lint-staged の設定が認識されない場合
フックは動いているのに lint-staged が意図した動作をしない場合、設定ファイルの場所が問題なことがあります。
lint-staged の設定は以下のいずれかで指定できます:
// package.json に直接書く(推奨)
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{js,jsx}": ["eslint --fix", "prettier --write"],
"*.{css,scss}": ["prettier --write"]
}
}
または独立した設定ファイル:
// .lintstagedrc.mjs
export default {
'*.{ts,tsx,js,jsx}': ['eslint --fix', 'prettier --write'],
'*.{css,scss,md}': ['prettier --write'],
}
注意: Husky v9 + lint-staged v15 以降、ESM 形式の設定ファイル(.lintstagedrc.mjs)でないと認識されないケースがあります。package.json に直接書く方法が最もトラブルが少なくおすすめです。
最終確認:現在の設定状態をチェックするコマンド
# フックが .git/hooks に正しくインストールされているか確認
ls -la .git/hooks/
# husky のバージョン確認
npx husky --version
# lint-staged の動作を手動でテスト(コミットせずに実行)
npx lint-staged --debug
ls -la .git/hooks/ で pre-commit が .husky/pre-commit へのシンボリックリンクになっていれば、Husky のインストールは成功しています。
lrwxrwxrwx ... pre-commit -> ../../.husky/pre-commit
まとめ
Husky v9 で pre-commit フックが動かない原因と対策をまとめると:
| 原因 | 症状 | 対策 |
|---|---|---|
| v8 スタイルのフックファイル | husky.sh: not found |
npx husky init で再生成 |
| パーミッション不足 | hook was ignored because it's not set as executable |
chmod +x .husky/pre-commit |
| PATH が通っていない | code 127 (command not found) |
フックで export PATH を明示 |
| CI でフックが実行される | CI ビルドが落ちる | HUSKY=0 環境変数を設定 |
| prepare スクリプト未設定 | 新規クローン後に動かない | package.json に "prepare": "husky" を追加 |
Husky v9 は v8 と比べてシンプルになりましたが、移行時のつまずきポイントが集中しています。特に「v8 スタイルの husky.sh ソース読み込みの削除」は見落としやすいので、v8 → v9 移行時は必ず確認しましょう。
