ERR_PNPM_PEER_DEP_ISSUES: как починить и не сломать lockfile
Сообщение ERR_PNPM_PEER_DEP_ISSUES от pnpm пугает многих. Текст длинный, в красном цвете, со списком пакетов, которые «должны быть, но их нет» или «должны быть другой версии, чем у тебя установлена». Разбираю, что это значит и как чинить, не превращая package.json в кашу.
Что вообще такое peer dependency
Peer dependency — это «я хочу, чтобы вместе со мной у тебя был установлен такой-то пакет такой-то версии, но я не буду тащить его сам». Классический пример — плагин для React: react-foo объявляет peerDependency: react ^18, и значит «приложение должно само поставить React 18, я не дублирую».
В npm peer dependency раньше игнорировались, потом стали авто-устанавливаться. В pnpm же поведение по умолчанию строгое: если peer dep не удовлетворён, pnpm пишет о проблеме.
Как выглядит ошибка
ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies
.
├─┬ react-query
│ └── ✕ unmet peer react@"^17.0.0": found 18.2.0
└─┬ next-themes
└── ✕ missing peer react-dom@"^17.0.0"Что pnpm говорит:
react-queryхочет React 17, а у тебя 18. Несовпадение версий.next-themesхочет react-dom 17, а его нет в зависимостях вовсе.
Когда это критично, а когда нет
Не каждый peer-conflict — реальная проблема. Часто пакет работает с более новой версией, просто не успели обновить ограничение.
- Реальная проблема: пакет упадёт в рантайме или будет работать с багами.
- Шум: пакет работает прекрасно, peer-ограничение устарело и автор не выпустил релиз.
Ты узнаёшь, что именно у тебя, только проверив. Я обычно начинаю с issue-трекера пакета: «works with react 18?» — и почти всегда видно, что да, всё ок.
Способы починки
1. Установить недостающий peer
Если пакет реально хочет react-dom, и ты его забыл — поставь:
pnpm add react-dom@^18Базово помогает в случае «missing peer». pnpm увидит, что пакет теперь есть, и предупреждение исчезнет.
2. Обновить пакет, у которого устарело peer-ограничение
pnpm update react-query --latestЕсли автор уже выпустил релиз с поддержкой React 18 — обновление снимает проблему. Не забудь прочитать changelog и проверить тесты.
3. peerDependencyRules: ignoreMissing
Если ты точно знаешь, что peer dep не нужен — попроси pnpm не ругаться. В корне проекта в package.json:
{
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": ["some-optional-peer"]
}
}
}Использую только в крайнем случае, когда я уверен в отсутствии последствий.
4. peerDependencyRules: allowedVersions
Когда пакет работает с более новой версией, но автор пока не обновил ограничение:
{
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"react": "18",
"react-dom": "18"
}
}
}
}pnpm перестанет ругаться на конфликт, считая, что 18 ок для тех пакетов, которые требуют 17. Удобно для библиотек, которые отстали от своих peer-ограничений.
5. overrides
Если какой-то пакет тащит зависимость с известным багом, и ты хочешь форсировать другую версию:
{
"pnpm": {
"overrides": {
"some-buggy-package": "^2.5.0",
"another-package>some-buggy-package": "^2.5.0"
}
}
}Первая запись применяется ко всем употреблениям пакета, вторая — только к транзитивной зависимости конкретного родителя. Используй точечно: проще наделать дыр в дереве зависимостей, чем починить.
6. auto-install-peers
В .npmrc можно включить автоустановку peer dep:
auto-install-peers=truepnpm сам тащит peer dep при install. С одной стороны, удобно. С другой — добавляет в lockfile зависимости, которые ты явно не объявлял. На больших командах я бы оставил false и ставил peer dep вручную.
Реальный пример из работы
На одном проекте после апгрейда Next.js на 14 я ловил такой набор:
react@18.3.1
✕ unmet peer react@"^17.0.0" needed by react-helmet-async
✕ unmet peer react@"^17.0.0" needed by react-virtuosoЧто я сделал:
- Открыл changelog
react-virtuoso: с версии 4.x у них уже React 18 поддерживается. Сделалpnpm add react-virtuoso@^4. Перешло. react-helmet-asyncуже не обновляется. Я заменил его на встроенныйnext/head, проблема ушла как класс.
Никаких peerDependencyRules не понадобилось. Реальный анализ почти всегда выгоднее, чем «давай заглушим warning».
Когда warning — это просто шум
Бывает, что peer-conflict живёт годами, потому что пакет не обновляется. Если код работает, тесты зелёные, jq на CI ок — это шум. Тогда я в peerDependencyRules.allowedVersions явно прописываю «знаю, всё ок» и иду дальше.
Главное — не отключать предупреждения глобально. Каждое исключение должно быть подписано в комментарии git, чтобы потом не разбираться, зачем у тебя в pnpm-конфиге странный override.
strictPeerDependencies
Если у тебя в проекте включён строгий режим:
strict-peer-dependencies=truepnpm не просто warn-ит, а падает на install. Это полезно для библиотек, которые публикуются и должны иметь чистое дерево зависимостей. Для приложений я обычно оставляю warning-режим.
Lockfile
Любые изменения зависимостей коммить с lockfile-ом. pnpm-lock.yaml в git, всегда. Без него на CI ты получишь другие версии транзитивных зависимостей и можешь поймать совсем другие peer-проблемы.
Что делать с workspaces
На монорепе с workspaces peer-dep живёт отдельно у каждого пакета. Если ты ставишь зависимость в один пакет, peer-dep этого пакета может конфликтовать с тем, что есть в другом. pnpm в таком случае иногда поднимает дубли, иногда говорит о проблеме.
Правило: общие зависимости (react, типы) лучше держать в корне через shared-workspace или с одинаковыми ограничениями в каждом пакете. Иначе ловишь странные проблемы.
Шпаргалка
- Прочитай ошибку: missing или unmet?
- Missing — поставь peer-пакет.
- Unmet с устаревшим ограничением — обнови сам пакет, либо
allowedVersions. - Реально несовместимая версия — ищи альтернативу.
- Никогда не отключай предупреждения целиком.
peer-dep ошибки выглядят страшно, но 9 из 10 чинятся за пару минут. Главное — не пытаться обмануть pnpm, а действительно разобраться, почему пакеты не сходятся. Это занимает чуть больше времени один раз, но экономит часы дальнейшего дебага.