npm ERR! ERESOLVE unable to resolve dependency tree — что делать
Ошибка ERESOLVE unable to resolve dependency tree вылезает после npm install и обычно означает, что npm не может уложить версии пакетов так, чтобы все peer-зависимости совпали. Раньше npm в таких случаях молча ставил что было, начиная с npm 7 — отказывается ставить и кидает ошибку.
Я обычно сталкиваюсь с этой ошибкой при апгрейдах React или Next, реже — при добавлении старых UI-китов. Покажу, как читать сообщение и какие варианты починки нормальные, а какие — самострел.
Как читать вывод
Типичный вывод выглядит так:
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: my-app@0.1.0
npm ERR! Found: react@19.0.0
npm ERR! node_modules/react
npm ERR! react@"19.0.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.0 || ^18.0.0" from react-some-lib@2.3.0
npm ERR! node_modules/react-some-lib
npm ERR! react-some-lib@"2.3.0" from the root projectЗдесь читается так:
- в проекте уже стоит
react@19.0.0; - пакет
react-some-lib@2.3.0в peer-зависимостях ждётreact@^17 || ^18; - npm не понимает, как это совместить, и ругается.
Дальше — выбор: подобрать версию пакета, которая поддерживает React 19, или принудительно сказать npm «я знаю, что делаю».
Правильный путь: посмотреть, поддерживает ли пакет нужную версию
Прежде чем тыкать --force, иду в npm и смотрю свежие версии пакета. Часто бывает так:
- v2.3.0 поддерживает React 17–18;
- v3.0.0 (или beta) уже работает с React 19.
Если новая версия есть — обновляю:
npm install react-some-lib@latestВ большинстве случаев на этом всё заканчивается. Особенно после крупных мажоров React, когда у мейнтейнеров просто была пара недель на обновление peer-диапазонов.
Когда обновлений нет
Бывает, что пакет давно не обновляли. Тогда вариантов три, и я выбираю их в таком порядке:
1. Заменить пакет на живой аналог
Особенно актуально для UI-библиотек. Если автор пропал — тащить его наследие лет на пять вперёд проще не стоит.
2. --legacy-peer-deps
Это поведение npm 6: устанавливай, peer-несовпадения считай предупреждением, не ошибкой.
npm install --legacy-peer-depsЧтобы не вспоминать каждый раз, можно зафиксировать в .npmrc у проекта:
echo 'legacy-peer-deps=true' >> .npmrcМинус: ты явно говоришь npm «закрывай глаза на конфликты». Если пакет внутри ждёт API-методов из своей версии React, а у тебя другая — может рантайм-уронить.
3. overrides в package.json
Самый аккуратный способ — точечно зафиксировать версию проблемной зависимости через overrides. Подходит, когда сторонний пакет тащит зависимость, которую тебе хочется ровно ту же, что и у тебя в корне.
{
"overrides": {
"react": "19.0.0",
"react-some-lib": {
"react": "19.0.0"
}
}
}Дальше — npm install. Этот способ лучше --legacy-peer-deps, потому что ты явно фиксируешь решение, а не глобально отключаешь проверку.
Чего я никогда не делаю
--force. Эта опция говорит npm «делай как хочешь», и часто оставляет lockfile в странном состоянии. Симптомы — гуляющие версии, баги, которые воспроизводятся только у одного человека из команды. Если уж нужно ослабить проверку, я выбираю --legacy-peer-deps: он хотя бы предсказуемо ведёт себя только с peer-зависимостями.
И не удаляю package-lock.json «чтобы решилось». Удаление лока в попытке починить ERESOLVE обычно ничего не лечит, потому что ошибка — про peer-диапазоны в самих пакетах, а не про лок.
Особый случай: монорепо и workspaces
В монорепе ERESOLVE может прилететь, потому что один из workspace-пакетов фиксирует одну версию зависимости, а другой — другую. Здесь стандартный путь:
- привести версии к общему знаменателю в
package.jsonкаждого пакета; - или использовать
resolutions(Yarn) /overrides(npm) на уровне корня.
В моей последней монорепе с pnpm я обычно ставлю strict-peer-dependencies=false на этапе разработки, а в CI прогоняю pnpm install --frozen-lockfile — так на миграциях вижу проблемы рано, а не на бою.
Чек-лист, который у меня всегда под рукой
- Прочитал стек ошибки, понял какой пакет конфликтует.
- Проверил последнюю версию пакета — может, уже починили.
- Если пакет жив — обновил.
- Если нет — заменил на живой или прописал
overrides. --legacy-peer-deps— только если уверен, что это безопасно.--force— никогда.
Если правильно прочитать сообщение, ERESOLVE даже полезен: он не даёт собрать заведомо несовместимый набор пакетов. Без него такие штуки всплывают через две недели на проде в виде «у нас опять упал чекаут».