lenec ru

← все посты

EACCES permission denied на macOS при npm — как починить правильно

18K

Самый невинный сценарий: ставишь глобальный пакет через npm install -g и получаешь:

npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/...'

Привычный рефлекс — приписать sudo. Этот путь ведёт ровно в ад, который потом неделю выгребаешь. Расскажу, почему так делать не надо, и что делать вместо.

Почему sudo npm install — плохо

Файлы, поставленные с правами root, остаются с правами root. Когда тот же npm потом попытается обновиться, проверить лок-файл, или бинари ускоренно записать — он падает на permission denied уже при чтении. Дальше начинается каскад: ты ставишь под root всё больше, пока в кэшах не появляется куча файлов, до которых ты не можешь дотянуться без sudo.

На моей предыдущей машине я года три держала смесь root- и user-файлов в ~/.npm, и каждое обновление было приключением.

Правильный путь — сделать так, чтобы npm никогда не лез туда, где у пользователя нет прав.

Способ 1: использовать менеджер версий Node

Самый простой и универсальный путь. nvm, fnm, volta ставят Node в твою домашнюю директорию. Глобальные пакеты приземляются туда же, под твоего пользователя — никаких EACCES никогда.

Установка fnm через Homebrew:

brew install fnm
fnm install --lts
fnm default $(fnm current)

В ~/.zshrc добавь активацию:

eval "$(fnm env --use-on-cd)"

После этого npm install -g typescript ставится в ~/.local/share/fnm/aliases/..., никакого root не требуется.

Способ 2: настроить prefix npm в свой каталог

Если не хочется ставить менеджер версий и текущий node устраивает, можно сказать npm: «ставь всё глобальное в мою домашнюю директорию».

mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global

Затем добавь ~/.npm-global/bin в PATH через ~/.zshrc:

export PATH="$HOME/.npm-global/bin:$PATH"

Перезапусти shell. Теперь npm install -g typescript кладёт бинарник в ~/.npm-global/bin/tsc, и shell его находит без всяких прав.

Способ 3: вообще не ставить пакеты глобально

Я уже несколько лет почти не использую -g. Все нужные тулы — TypeScript, Astro CLI, Drizzle Kit — у меня в devDependencies проекта, и я зову их через npx, pnpm exec или скрипты в package.json:

{
  "scripts": {
    "build": "tsc -p .",
    "db:push": "drizzle-kit push"
  },
  "devDependencies": {
    "typescript": "5.6.0",
    "drizzle-kit": "0.27.0"
  }
}

Плюсы: версия инструмента совпадает с проектом, не нужно глобально что-то ставить, на новой машине достаточно pnpm install — и всё уже доступно.

Если очень нужен глобальный CLI (gh, vercel, expo), я ставлю его через Homebrew, не через npm:

brew install gh
brew install vercel-cli

Brew управляет правами и путями за меня.

Восстановление прав, если уже накосячила с sudo

Если в ~/.npm часть файлов с root-владельцем, верни всё себе:

sudo chown -R $(whoami) ~/.npm ~/.config
sudo chown -R $(whoami) /usr/local/lib/node_modules

(второй пример — только если ты сама ставила туда глобально и хочешь там всё забрать обратно).

Дальше можно либо переехать в ~/.npm-global (см. способ 2), либо снести Node и поставить через fnm, и стартовать с чистого листа.

Особый случай: брю-Node против системного

На моей сборке какое-то время одновременно были Node от brew (/opt/homebrew/bin/node) и от установщика с nodejs.org (/usr/local/bin/node). PATH перепутывался, npm то ставил пакеты в один префикс, то в другой, ловила EACCES в самых разных местах.

Лекарство — выбрать что-то одно. Я перешла на fnm и снесла оба:

brew uninstall node
# и вручную удалила то, что от установщика nodejs.org

После этого жизнь стала проще.

Что делать, если EACCES прилетел не на установку, а на запуск

Иногда EACCES появляется при работе с файлами проекта — например, npm пытается записать в node_modules/.package-lock.json, а у файла стоят чужие права.

Это происходит, если кто-то делал sudo npm install в этом каталоге раньше. Чинится так:

sudo chown -R $(whoami) node_modules
rm -rf node_modules
pnpm install # или npm install

Чек-лист

  • Не запускать sudo npm. Никогда. Совсем.
  • Если есть выбор — ставить Node через fnm/nvm/volta или через Homebrew.
  • Если нужен npm prefix — ~/.npm-global и PATH к нему.
  • Глобальные тулы держать минимально, остальное — в devDependencies.
  • Если уже накосячила — chown -R $(whoami) для проблемных директорий.

EACCES — это не баг npm, это сигнал «ты пытаешься писать туда, где у тебя нет прав». Решение — не получать новые права через sudo, а класть npm-пакеты туда, где права уже есть. После правильной начальной настройки EACCES не появляется месяцами.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.