macOSでネイティブNode.jsモジュールをビルドする際の「clang: error: linker command failed with exit code 1」を修正する

intermediate🍎 macOS2026-05-11| macOS 12〜15、Node.js 16〜22、npm/yarn/pnpm、ネイティブNode.jsモジュール(node-gyp、node-pre-gyp)

Error Message

clang: error: linker command failed with exit code 1 (use -v to see invocation)
#node#npm#ネイティブモジュール#clang#ビルド#macos

エラーの内容

npm install または npm rebuild を実行すると、ビルドが次のエラーで失敗します:

gyp info spawn args   '-Wno-unused-variable',
gyp info spawn args   '-arch', 'arm64'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2

ネイティブ Node.js モジュール — sharpbcryptsqlite3canvasnode-sass のように C/C++ コードをコンパイルするもの — は、コンパイルとリンクの2段階でビルドされます。このエラーはリンクが失敗したことを意味します。オブジェクトファイルは存在していますが、clang が最終的なバイナリにまとめることができませんでした。

根本原因

macOS のアップグレードが引き金になることがほとんどです。複数の問題が同時に発生する場合があります:

  • Xcode コマンドラインツールが未インストール、古い、または macOS アップデート後に静かに破損している
  • macOS SDK のパスが変わり、clang がシステムライブラリを見つけられなくなった
  • アーキテクチャの不一致 — Apple Silicon(M1/M2/M3)上で x86_64 向けにビルドしようとしている、またはその逆
  • ネイティブモジュールがリンクするシステムライブラリやフレームワークが見つからない
  • Node.js のバージョンと node-gyp のバージョンに互換性がない
  • Python のバージョンが間違っている(node-gyp は Python 3.x が必要で、2.x は非対応)

修正1: Xcode コマンドラインツールを再インストールする

特に macOS アップグレード直後は、これだけで解決することがほとんどです。

# 既存の(壊れている可能性がある)インストールを削除
sudo rm -rf /Library/Developer/CommandLineTools

# 新規インストールを開始
sudo xcode-select --install

確認ダイアログが表示されます。完了したら:

# パスが正しく設定されているか確認
xcode-select -p
# 出力されるべき値: /Library/Developer/CommandLineTools

# clang が動作するか確認
clang --version

npm install を再試行してください。成功すれば完了です。

修正2: Xcode ライセンスの同意と SDK パスのリセット

フル Xcode(CLT だけでなく)にはライセンス同意が必要で、保留中の場合はビルドが静かにブロックされることがあります:

sudo xcodebuild -license accept

次に、xcode-select が指しているパスを確認します:

# フル Xcode がインストールされている場合
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

# フル Xcode が不要な場合は CLT に戻す
sudo xcode-select -s /Library/Developer/CommandLineTools

修正3: SDK パスを手動で固定する

clang は自身の場所を把握していても、SDK を見失うことがあります。特に macOS の差分アップデートで SDK バージョンが MacOSX13.sdk から MacOSX14.sdk に変わった場合に起きやすいです。明示的に固定してください:

# 現在の SDK パスを確認
xcrun --show-sdk-path
# 例: /Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk

# ビルド前にエクスポート
export SDKROOT=$(xcrun --show-sdk-path)
npm install

次回のターミナルセッションでも壊れないよう、永続的に設定します:

echo 'export SDKROOT=$(xcrun --show-sdk-path)' >> ~/.zshrc
source ~/.zshrc

修正4: Apple Silicon でのアーキテクチャ不一致

M1/M2/M3 Mac は arm64 で動作します。Node.js が Rosetta または x86_64 シェル経由でインストールされていると、リンカーが誤ったアーキテクチャをターゲットにして失敗します。

# Node.js のアーキテクチャを確認
node -e "console.log(process.arch)"
# Apple Silicon では 'arm64' が表示されるべき('x64' ではない)

# Rosetta 上で動作しているか確認
rosetta --version 2>/dev/null || echo "Not using Rosetta"

arm64 Mac で x64 と表示された場合は、Node.js をネイティブで再インストールしてください。Homebrew の場合:

# Homebrew 自体が arm64 かどうか確認
file $(which brew)
# 表示されるべき内容: Mach-O 64-bit executable arm64

brew uninstall node
brew install node

nvm の場合:

nvm install 20 --default
nvm use 20
node -e "console.log(process.arch)"  # arm64

修正5: npm キャッシュのクリアとクリーンビルド

壊れたビルド成果物がキャッシュに残ることがあります。根本原因を修正した後でも、npm が同じ壊れたビルドをキャッシュから再生する可能性があります。

npm cache clean --force
rm -rf node_modules
npm install

全体を再インストールせず、特定のパッケージだけを対象にする場合:

npm rebuild sharp
# または
npm rebuild bcrypt

修正6: node-gyp を更新する

古いバージョンの node-gyp は、新しい Node.js リリースや更新された SDK パスを認識しないことがあります。たとえば Node.js 22 には node-gyp 10 以上が必要です。

npm install -g node-gyp@latest

# 更新されたバージョンで再ビルド
node-gyp rebuild

パッケージによっては古い node-gyp を内包していることがあります。次のようにして上書きできます:

npm_config_node_gyp=$(npm root -g)/node-gyp/bin/node-gyp.js npm install

修正7: 不足しているライブラリを見つける

詳細な出力を確認すると、リンカーが何を見つけられなかったかが正確にわかります。汎用的な終了コードよりもはるかに有用です:

npm install --verbose 2>&1 | grep -A5 'linker command failed'

またはエラー自体が提案する -v フラグを使って clang を実行してください。macOS のクリーンインストール後によくある原因:

  • OpenSSL(macOS 12 以降に未搭載): brew install openssl の後、export OPENSSL_ROOT_DIR=$(brew --prefix openssl)
  • libvips(sharp 用): brew install vips
  • libpng / libjpeg: brew install libpng libjpeg

インストール後、pkg-config を新しいライブラリに向けて再ビルドします:

export PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig:$PKG_CONFIG_PATH"
npm rebuild

動作確認

完了と判断する前に、簡単なチェックを行いましょう:

# 末尾20行を確認 — エラーはすぐに目に入る
npm install 2>&1 | tail -20

# モジュールを読み込んでみる
node -e "require('your-module-name'); console.log('OK')"

# bcrypt での具体的な例
node -e "const b = require('bcrypt'); console.log(b.getRounds(b.hashSync('test', 10)))"

例外が発生しなければ、リンクは成功しており、モジュールは正常に動作しています。

予防策

  • macOS をアップグレードするたびに、事前に sudo xcode-select --install を実行してください — 深夜2時にビルドが失敗してから対処するのでは遅いです
  • SDK パスの変更が将来のインストールを静かに壊さないよう、シェルプロファイルに SDKROOT を設定しておきましょう
  • チーム全体で Node.js のアーキテクチャを統一してください — README に期待される process.arch を明記しましょう
  • ネイティブモジュールを自身でメンテナンスしている場合は、devDependencies で node-gyp を新しいバージョンに固定してください
  • GitHub Actions では、Xcode CLI ツールを明示的にインストールするか、それらがプリインストールされた macOS ランナーイメージを使用してください

Related Error Notes