Error 500 en Vercel API: build verde, endpoint roto
Un desarrollador independiente documentó algo que cualquiera que haya deployado APIs serverless en Vercel teme en silencio: build verde, logs impecables, TypeScript sin errores, y todos los endpoints devolviendo un hermoso error 500 en producción. El culpable no fue un bug de código ni una mala configuración de entorno. Fue Node File Trace, el sistema de empaquetado de Vercel, que solo incluye archivos alcanzados mediante imports de node_modules y dejó afuera los imports relativos entre carpetas hermanas.
Cuando ocurre un error 500 en APIs de Vercel con build exitoso, la causa puede ser Node File Trace (NFT), el mecanismo interno que Vercel usa para armar el bundle de cada función serverless. NFT solo sigue imports que resuelven desde node_modules (imports “bare”), e ignora imports relativos que cruzan fuera del árbol de archivos de la función, como un helper en ../lib/sentry.ts llamado desde api/users/route.ts. El resultado: deploy impecable, endpoint roto, y ERR_MODULE_NOT_FOUND como único mensaje de bienvenida. La documentación oficial no lo advierte con claridad, y ni tsc --noEmit ni los logs de build lo detectan.
En 30 segundos
- Node File Trace (NFT) es el que decide qué archivos entran en cada función serverless. Solo sigue imports desde node_modules e ignora imports relativos entre carpetas como /api y /lib.
- Cuatro estrategias “lógicas” fallan en producción: carpeta lib/ compartida, api/_lib con guion bajo, includeFiles en vercel.json, y renombrar con underscore. Todas dan build verde y error 500 al primer request.
- Dos soluciones funcionan realmente: inlinear el código helper en cada handler, o usar un paquete workspace con
file:./packagesen package.json para que el import resuelva vía node_modules. - Ni TypeScript ni los logs de build detectan esto. La única forma de atraparlo antes del desastre es un smoke test contra la URL deployada, pidiendo JSON real y validando la respuesta.
¿Por qué Vercel devuelve 500 en endpoints API si el build fue exitoso?
Imaginate esta escena: subís tu proyecto, Vercel te muestra el checkmark verde, el log dice “Ready” en todas las funciones, respirás aliviado y te vas a dormir. Al día siguiente, un cliente te escribe: “la API no responde”. Abrís el dashboard de Vercel y ves una cascada de 500 en cada endpoint que usa un helper compartido.
El problema, documentado por el fundador de AimVantage, es que Node File Trace construye el bundle de cada función como si fuera un universo aislado. Si tu handler en api/users/route.ts importa algo de ../../lib/sentry.ts, NFT no incluye ese archivo en el bundle porque considera que está “fuera del árbol” de la función. El build pasa, el deploy se completa, y al primer request real el runtime no encuentra el módulo. Error 500.
Lo más frustrante es el silencio. No hay warning en el build, no hay flag en el dashboard, no hay nada que te diga “che, este archivo que estás importando no va a estar disponible en producción”. Simplemente se rompe cuando alguien lo usa. ¿Y cómo se enteró el desarrollador de AimVantage? Probando. A mano. Después de varios intentos fallidos. Lo explicamos a fondo en desplegar tu web gratis en Vercel.
¿Qué es Node File Trace (NFT) y cómo decide qué archivos empaquetar?
Node File Trace es el sistema que Vercel usa para determinar qué archivos necesita cada función serverless y empaquetar solo lo indispensable. La idea es noble: reducir el tamaño del bundle para que el cold start sea más rápido y el deploy más liviano. El problema es cómo lo hace.
NFT analiza el árbol de dependencias de cada archivo de entrada, pero tiene una regla interna que muchos desconocen: solo sigue imports que resuelven a través de node_modules. Es decir, si hacés import { log } from 'mi-lib' y mi-lib está en node_modules, NFT lo incluye sin drama. Pero si hacés import { log } from '../../lib/sentry', donde lib/ es una carpeta hermana de api/, el import es “relativo cruzando árboles” y NFT lo ignora (spoiler: por diseño, no por bug).
Esto, que en teoría optimiza el empaquetado, se convierte en una trampa cuando tenés helpers compartidos entre funciones. Vos ves tu estructura de carpetas y pensás “esto es normal, es DRY, es prolijo”. NFT ve archivos que no pertenecen al scope estricto de la función y los deja afuera.
Estrategias que parecen funcionar en local pero fallan en producción
El desarrollador de AimVantage probó varios enfoques distintos antes de entender qué pasaba. Todos fallaron con el mismo patrón: build exitoso y error 500 al primer request. Acá van, para que no pierdas el tiempo como él: Más contexto en nuestra comparativa de CI/CD.
- Carpeta lib/ en la raíz con import relativo. La estructura más obvia:
src/lib/sentry.tsimportado desdesrc/api/users/route.tscon../../lib/sentry. NFT no sigue el import, el archivo no entra en el bundle. - Carpeta api/_lib con guion bajo. El prefijo underscore supuestamente evita que Vercel trate la carpeta como una ruta. Falso: NFT sigue ignorando imports relativos que salen del handler hacia ahí.
- includeFiles en vercel.json. Configuraste un glob para incluir archivos adicionales. El build lo acepta, pero NFT lo ignora porque includeFiles aplica al deploy entero, no al bundle individual de cada función serverless.
- Renombrar con guion bajo para esquivar el límite de funciones. Tampoco. El underscore no le cambia la lógica a NFT, solo afecta el routing de Vercel.
El patrón común es que todas estas estrategias asumen que Vercel respeta la estructura de archivos que vos definiste. No es el caso. NFT impone su propia lógica, y si no la entendés, terminás con un deploy que parece sano pero está roto desde el minuto cero.
Las únicas dos soluciones que realmente funcionan para el error 500 en Vercel API
Después de varios intentos fallidos, el fundador de AimVantage identificó dos approaches que sobreviven al empaquetado de NFT sin tirar error 500 en producción. Ninguno es ideal, pero los dos funcionan.
Solución 1: inlinear el helper en cada handler. Copiás el código del helper directamente dentro de cada archivo de función. El bundle es autónomo, NFT solo tiene que resolver imports de node_modules (que sí sigue sin problemas) y el deploy sale limpio. La contra: si tenés seis endpoints que usan el mismo helper de Sentry, un cambio en la lógica de logging te obliga a tocar seis archivos. Para proyectos chicos o MVPs, zafa. Para cosas más grandes, se vuelve una pesadilla de mantenimiento.
Solución 2: paquete workspace con file:./packages en package.json. Creás una carpeta packages/mi-lib/ con su propio package.json, y en el package.json raíz referenciás "mi-lib": "file:./packages/mi-lib". Después instalás con npm install o yarn, y el paquete aparece en node_modules. Cuando tu handler hace import { log } from 'mi-lib', el import es “bare” (resuelve desde node_modules) y NFT lo empaqueta sin chistar. Es más setup inicial, pero gana en modularidad y mantenibilidad. Te puede servir nuestra cobertura de el debate entre Jenkins y GitHub Actions.
| Solución | Cómo funciona | Ventaja | Desventaja |
|---|---|---|---|
| Inline del helper | Código duplicado en cada handler | Setup inmediato, cero configuración extra | Cambios requieren tocar N archivos |
| Workspace package | Import vía node_modules con file:./packages | Modular, mantenible, NFT lo procesa nativo | Requiere armar estructura de paquete |

Cómo detectar este error antes del deploy: el valor de las pruebas de humo
Acá viene lo bueno: ni tsc --noEmit ni los logs de build de Vercel pueden detectar este problema. TypeScript ve los imports, resuelve los tipos, y da todo OK porque en tiempo de compilación los archivos existen y están bien referenciados. Los logs de build solo confirman que el empaquetado terminó, no que cada función vaya a encontrar sus dependencias en runtime.
La única defensa real es un smoke test automatizado. Algo tan simple como un script que, después del deploy, haga un fetch a cada endpoint y valide que la respuesta sea un JSON con la estructura esperada. Si el endpoint devuelve 500, el smoke test falla y frenás todo antes de que el error llegue a producción real.
Un ejemplo concreto, basado en lo que armó el desarrollador de AimVantage: un script en Node que itera sobre los endpoints definidos en una constante, hace fetch(url), verifica response.ok y Content-Type: application/json, y corta con process.exit(1) si algo falla. Metés eso en tu pipeline de CI/CD como npm run smoke y te ahorrás el disgusto de enterarte por un cliente.
¿Qué hacer si tu proyecto ya está en producción y encontrás este error?
Si ya tenés usuarios viendo 500, no es momento de reescribir la arquitectura entera. Hacé esto: En la guía de hreflang para SEO profundizamos sobre esto.
- Revisá los logs de runtime en el dashboard de Vercel. Buscá
ERR_MODULE_NOT_FOUND. Ahí vas a ver exactamente qué archivo falta y desde qué handler lo están pidiendo. - Identificá todos los endpoints afectados. No asumas que es uno solo. Si compartías helpers entre varias funciones serverless, probablemente todos estén rotos.
- Elegí una de las dos soluciones que funcionan (inline o workspace package) y migrá los helpers compartidos. Para un arreglo de emergencia, inlinear es más rápido.
- Redeploy y ejecutá el smoke test contra la URL de producción. No confíes en el checkmark verde del dashboard; hacé requests reales.
Qué está confirmado y qué no sobre este error 500 en Vercel
- Confirmado: Node File Trace ignora imports relativos que cruzan fuera del árbol de la función serverless. Lo confirmó un desarrollador con pruebas reproducibles.
- Confirmado: Las soluciones de inline y workspace package funcionan. Están validadas con deploys reales en producción.
- Pendiente: No hay fecha confirmada para un cambio en el comportamiento por defecto de NFT.
- Pendiente: No está claro si includeFiles en vercel.json va a ser respetado por NFT en futuras versiones o si la solución será otra.
Errores comunes al deployar funciones serverless en Vercel
- Confiar ciegamente en el checkmark verde del build. El build solo confirma que no hay errores de compilación. No significa que los módulos estén disponibles en runtime. Si no hacés smoke tests contra la URL real, estás deployando a ciegas. Esto aplica a Vercel y a cualquier plataforma serverless, incluyendo servicios de hosting cloud como donweb.com donde también conviene validar los endpoints después del deploy.
- Asumir que includeFiles en vercel.json cubre imports entre funciones. includeFiles agrega archivos al deploy general, no al bundle individual de cada función. Son dos capas distintas y NFT opera en la segunda. Leer “includeFiles” y pensar “ya está, mis helpers van a estar disponibles” es un error clásico.
- No revisar los logs de runtime en producción. El dashboard de Vercel tiene pestañas separadas para build y para runtime. Si solo mirás la de build, no vas a ver los
ERR_MODULE_NOT_FOUNDque aparecen cuando un endpoint recibe su primer request. Acostumbrate a revisar ambas.
Preguntas Frecuentes
¿Por qué mi API en Vercel devuelve 500 si el build dice Ready?
Porque Node File Trace (NFT) no empaquetó los imports relativos que cruzan fuera del árbol de la función serverless. El build termina sin errores porque TypeScript encuentra los archivos en tiempo de compilación, pero en runtime el módulo no está en el bundle y salta ERR_MODULE_NOT_FOUND.
¿Cómo compartir helpers entre funciones serverless de Vercel sin que fallen?
Tenés dos opciones que funcionan: inlinear el código del helper directamente en cada handler (funciones autocontenidas), o crear un paquete workspace con file:./packages en package.json para que el import resuelva desde node_modules y NFT lo procese sin problemas.
¿Qué es Node File Trace y cómo afecta mis imports relativos en Vercel?
Node File Trace es el sistema de Vercel que decide qué archivos incluir en el bundle de cada función serverless. Solo sigue imports que pasan por node_modules (imports bare) e ignora imports relativos que cruzan a carpetas hermanas como ../../lib/sentry desde una función en api/.
¿Cómo saber si mi deploy de Vercel realmente funciona sin probar cada endpoint?
La única forma es un smoke test automatizado que haga requests reales a cada endpoint deployado y valide la respuesta. tsc --noEmit y los logs de build no detectan este tipo de error. Integrá el smoke test en tu pipeline de CI/CD para frenar deploys rotos antes de que lleguen a producción.
¿Cuáles son las soluciones oficiales para compartir código en funciones serverless de Vercel?
Vercel recomienda usar paquetes workspace (monorepo) para compartir código entre funciones. Por ahora la solución oficial es que los imports pasen por node_modules.
Conclusión
El error 500 silencioso de Vercel es uno de esos problemas que te hacen perder horas (o días) porque todo indica que el deploy salió bien. La raíz está en Node File Trace y su forma de decidir qué archivos entran en cada bundle, algo que la documentación no explica con la claridad que debería.
Lo positivo es que hay dos soluciones probadas. Mientras tanto, la lección es clara: no confíes en el checkmark verde, hacé smoke tests contra producción real, y si compartís código entre funciones serverless, asegurate de que los imports pasen por node_modules o inlineá sin miedo. Lo que no podés hacer es asumir que un import relativo entre carpetas va a funcionar, porque NFT piensa distinto que vos.





![[P] Built GPT-2, Llama 3, and DeepSeek from scratch in PyTorch - open source code + book - ilustracion](https://donweb.news/wp-content/uploads/2026/04/construir-llm-desde-cero-pytorch-gpt2-llama3-deepseek-hero-768x429.jpg)
