Creas un worktree de Git para trabajar en otra rama, enlazas su carpeta node_modules a la del repositorio principal para no reinstalar dependencias y ejecutas next dev o next build. Next.js 16 se detiene con un error parecido a este:
FATAL: An unexpected Turbopack error occurred.
Error [TurbopackInternalError]: Symlink [project]/node_modules is invalid, it points out of the filesystem root
El texto exacto cambia un poco entre versiones; por ejemplo, puede aparecer sin el prefijo [project]/. Este artículo explica por qué Turbopack rechaza ese enlace y presenta cuatro formas de resolverlo, con lo que cuesta cada una.
Por qué pasa
Desde Next.js 16, Turbopack es el empaquetador por defecto tanto en next dev como en next build. Turbopack resuelve los módulos dentro de una carpeta raíz, y la documentación de Next.js es explícita: los archivos que quedan fuera de la raíz del proyecto no se resuelven. Next.js detecta esa raíz buscando uno de estos archivos: pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lock o bun.lockb.
Un node_modules que es un enlace simbólico hacia otro checkout apunta a una carpeta situada fuera de esa raíz. La sección de la documentación de Turbopack sobre las diferencias con webpack se titula "Filesystem Root" y pone como ejemplo los paquetes enlazados fuera del proyecto con npm link, yarn link o pnpm link, que no se resuelven por defecto. El mensaje de error usa esa misma expresión. Si tu enlace es una junction de Windows, el planteamiento es igual: el destino sigue estando fuera de la raíz.
El truco del enlace es tentador con los worktrees porque git worktree add crea una carpeta de trabajo nueva con los archivos versionados, y node_modules suele estar ignorada por Git, así que no viaja con ella. En un reporte público del mismo error, su autor comenta que ese esquema funcionaba con builds basados en webpack; con Turbopack ya no.
Solución
Las opciones van de más a menos recomendable. Después de aplicar cualquiera de ellas, haz el último paso (borrar .next).
-
Instala las dependencias en cada worktree. Primero elimina el enlace sin tocar la carpeta a la que apunta. Si borras contenido a través del enlace, dañas el checkout principal. Estos comandos quitan solo el enlace y fallan si
node_modulesfuera una carpeta real con contenido:# Linux, macOS o WSL (sin -r y sin barra final) rm node_modules:: Windows, en cmd (sin /s) rmdir node_modulesDespués instala:
pnpm installpnpm no copia cada paquete en cada proyecto. Según su documentación, guarda los archivos una sola vez en un almacén global direccionable por contenido (content-addressable store) y crea enlaces duros desde el
node_modulesde cada proyecto hacia ese almacén. Por eso instalar en un worktree nuevo ocupa poco disco y descarga poco si las versiones ya están en el almacén. Un detalle importante: los enlaces duros solo existen dentro de un mismo sistema de archivos. Si el almacén y el proyecto están en discos distintos, pnpm copia los archivos y la instalación pesa más y tarda más. -
Copia
node_modulescon enlaces duros. En Linux o WSL, con GNUcp, y con origen y destino en el mismo sistema de archivos:cp -al ../mi-app/node_modules ./node_modulesLa opción
-aconserva la estructura y copia los enlaces simbólicos como enlaces;-lcrea enlaces duros en lugar de duplicar el contenido. Obtienes una carpeta real dentro del proyecto, así que Turbopack ya no ve un enlace que sale de la raíz, y casi no gastas disco. Limitaciones:- Si origen y destino están en sistemas de archivos distintos, el comando falla.
- Los archivos enlazados son los mismos en ambas carpetas. Si una herramienta modifica un archivo de
node_modulesen su sitio (un parche, un generador de código), el cambio también aparece en el otro checkout. - Los enlaces simbólicos internos se copian tal cual. Si alguno apunta a una ruta absoluta del checkout original, seguirá apuntando allí. Revísalo con
ls -loreadlink.
-
Compila con webpack, como solución provisional. La CLI de Next.js documenta la opción
--webpackpara ambos comandos:next dev --webpack next build --webpackEl costo es que dejas de probar con el empaquetador que usa tu producción, si ahí usas Turbopack. La documentación enumera diferencias entre ambos (por ejemplo, el orden de los módulos CSS o la precisión decimal) y funciones que solo existen con Turbopack, como
import.meta.glob. Úsala para destrabarte, no para validar una entrega. -
Amplía la raíz de Turbopack. La documentación indica que, para resolver archivos de dependencias enlazadas fuera del proyecto, puedes apuntar
turbopack.roota la carpeta padre que contiene ambos lugares:// next.config.js const path = require('path') module.exports = { turbopack: { root: path.join(__dirname, '..'), }, }No amplíes la raíz más de lo necesario (por ejemplo, hasta tu carpeta de usuario): la documentación advierte que Turbopack puede vigilar todos los archivos dentro de las raíces y que una raíz demasiado grande causa problemas de rendimiento. Además, la página general de Turbopack reserva
turbopack.rootpara aplicaciones que importan archivos de fuera de la raíz detectada de forma directa, y recomienda otra opción para las dependencias alcanzadas mediante enlaces simbólicos: la opción experimentalexperimental.turbopackAdditionalRoots, descrita en la documentación de Next.js 16.4, que permite seguir enlaces cuyo destino está fuera deturbopack.root:// next.config.js const path = require('path') module.exports = { experimental: { turbopackAdditionalRoots: { checkoutPrincipal: { path: path.join(__dirname, '../mi-app') }, }, }, }Esta segunda opción es experimental y no existe en versiones anteriores a 16.4.0. No se verificó esta configuración con una estructura concreta de worktrees, así que pruébala en la tuya antes de depender de ella. Tampoco la confundas con
outputFileTracingRoot: según la documentación, esa opción controla qué archivos se incluyen al trazar dependencias para el despliegue en monorepos, no la resolución de módulos de Turbopack. -
Borra la salida anterior y vuelve a arrancar. Turbopack guarda caché de compilación dentro de
.next, y esa carpeta puede conservar resultados de intentos anteriores con la configuración vieja:rm -rf .nextRemove-Item -Recurse -Force .next
Cómo verificar que quedó resuelto
Primero comprueba que node_modules ya no es un enlace que sale del proyecto (salvo que hayas elegido la opción 4):
ls -ld node_modules
(Get-Item node_modules).LinkType
En Linux, la primera columna debe empezar con d (carpeta) y no con l (enlace). En PowerShell, el resultado debe estar vacío si es una carpeta real. Después ejecuta:
next dev
next build
El servidor de desarrollo debe arrancar y la compilación debe terminar sin el error FATAL. En un caso real, con la copia por enlaces duros el servidor de desarrollo arrancó y las pruebas funcionales completas del worktree se ejecutaron sin problemas; con --webpack, la compilación terminó correctamente.
Variantes y errores relacionados
El mismo mensaje con otros archivos
El error no es exclusivo de node_modules. Se ha reportado también con otros archivos enlazados, como un package.json. La causa es la misma: un enlace cuyo destino queda fuera de la raíz que usa Turbopack.
Almacén virtual global de pnpm
La documentación de turbopackAdditionalRoots menciona como caso de uso los paquetes enlazados que viven fuera del proyecto, con npm link o con el almacén virtual global de pnpm. Si usas esa función de pnpm y aparece el mismo error, es coherente con esta causa: los enlaces de node_modules apuntan fuera del proyecto.
Copias con enlaces duros y herramientas que escriben en node_modules
Si usas la opción 2 y ves cambios inesperados en el otro checkout después de instalar o generar código, es probable que ambas carpetas compartan archivos. En ese caso vuelve a la opción 1: una instalación por worktree es más predecible.
Resumen
- Turbopack, el empaquetador por defecto de Next.js 16, no resuelve archivos fuera de su raíz; un
node_modulesenlazado hacia otro checkout cae en ese caso. - La solución más robusta es instalar dependencias en cada worktree; el almacén de pnpm usa enlaces duros, así que es barato si está en el mismo disco.
- Alternativas:
cp -alen el mismo sistema de archivos,--webpackcomo parche temporal o ampliar la raíz conturbopack.root. - Al borrar un enlace, quita solo el enlace y no el contenido de su destino.
- Tras cualquier cambio, borra
.nexty verifica connext devynext build.
Comentarios
Publicar un comentario