En Node.js 26, fetch() falla con TypeError: fetch failed y la causa invalid onError method (UND_ERR_INVALID_ARG) cuando recibe un dispatcher creado por un paquete npm undici antiguo: un Agent normal, un agente keep-alive, una protección contra SSRF, el agente que incluye una biblioteca cliente o, lo más habitual, un agente de proxy. La petición nunca sale del proceso. Cada versión, mensaje de error y resultado de abajo procede de una ejecución en laboratorio del 7 de noviembre de 2026 o de la documentación y los issues enlazados.
Solución rápida
node -p "process.versions.undici"
npm ls undiciSi el primer comando imprime 8.x y el segundo muestra undici@5 o undici@6, esa copia es el origen probable. La solución que funcionó con todas las versiones de undici en las dos líneas de Node de nuestras pruebas es importar fetch del mismo paquete que crea el dispatcher: import { fetch, ProxyAgent } from 'undici' (la solución A, más abajo). Si el dispatcher solo existe para leer HTTPS_PROXY, elimínalo y arranca Node con --use-env-proxy.
Probado con Node.js 26.13.0 (undici 8.12.2 integrado) y 24.22.2 (undici 7.30.1 integrado), con npm undici 5.29.0, 6.29.1, 7.30.2 y 8.12.4, en macOS 26.5.1 arm64, el 7 de noviembre de 2026. Los scripts y los 166 casos registrados están en la carpeta del laboratorio.
El error: UND_ ERR_ INVALID_ ARG en err.cause
Esta es la salida completa de la reproducción mínima que aparece más abajo, que pasa un Agent de npm undici 6.29.1 al fetch integrado de Node.js 26.13.0. La ruta del laboratorio está abreviada como /lab:
node:internal/modules/run_main:111
triggerUncaughtException(
^
[TypeError: fetch failed] {
[cause]: InvalidArgumentError: invalid onError method
at Agent.dispatch (/lab/u6/node_modules/undici/lib/dispatcher/dispatcher-base.js:198:15)
at node:internal/deps/undici/undici:15144:57
at new Promise (<anonymous>)
at dispatchWithProtocolPreference (node:internal/deps/undici/undici:15144:18)
at dispatch (node:internal/deps/undici/undici:15142:16)
at httpNetworkFetch (node:internal/deps/undici/undici:15040:73)
at httpNetworkOrCacheFetch (node:internal/deps/undici/undici:14912:39)
at httpFetch (node:internal/deps/undici/undici:14729:43)
at schemeFetch (node:internal/deps/undici/undici:14637:18)
at mainFetch (node:internal/deps/undici/undici:14481:30) {
code: 'UND_ERR_INVALID_ARG',
Symbol(undici.error.UND_ERR): true,
Symbol(undici.error.UND_ERR_INVALID_ARG): true
}
}
Node.js v26.13.0La primera línea at nombra la copia de undici que lanzó el error: aquí /lab/u6/node_modules/undici. Con un ProxyAgent, la traza gana una línea, at ProxyAgent.dispatch, y el mensaje es el mismo. El mismo script en Node.js 24.22.2 imprimió 200. El código que captura el error y registra solo err.message imprime simplemente fetch failed; el motivo está en err.cause.
Por qué Node 26 rechaza los dispatchers de undici antiguos
El fetch integrado es una copia de undici incluida en Node, y process.versions.undici dice cuál. La opción dispatcher entrega cada petición al objeto que le pasas, así que dos versiones de undici tienen que ponerse de acuerdo en cómo hablan entre sí. undici 8 renombró los callbacks del handler que llama un dispatcher, por ejemplo onError pasó a ser onResponseError (guía de migración). El fetch de Node 26 pasa un handler solo con los callbacks nuevos. Un dispatcher de undici 5 o 6 busca el onError antiguo, no lo encuentra y lanza el error antes de abrir una conexión. Los dispatchers de undici 7 aceptaron los callbacks nuevos en nuestras pruebas.
La reproducción mínima:
import { Agent } from 'undici';
const res = await fetch('https://example.com/', { dispatcher: new Agent() });
console.log(res.status);Los mismos casos con Agent, ProxyAgent y EnvHttpProxyAgent, pasados por petición al fetch integrado:
| npm undici | Node.js 26.13.0 (undici 8.12.2) | Node.js 24.22.2 (undici 7.30.1) |
|---|---|---|
| 5.29.0 | invalid onError method | funciona |
| 6.29.1 | invalid onError method | funciona |
| 7.30.2 | funciona | funciona |
| 8.12.4 | funciona | invalid onRequestStart method |
undici 5.29.0 no tiene EnvHttpProxyAgent; todas las demás celdas se cumplieron con los tres tipos de agente. Las versiones antiguas se comportaron igual en Node 26: Agent y ProxyAgent de 5.0.0, 5.28.4, 6.0.0 y 6.21.0 fallaron con invalid onError method, y en las ocho versiones 7.x que probamos, de 7.0.0 a 7.28.0, funcionaron. La última fila es el mismo desajuste a la inversa: el fetch de Node 24 pasa un handler sin onRequestStart, y un dispatcher de undici 8 lo rechaza.
Encuentra qué incluye un undici antiguo
Ejecuta los dos comandos con el mismo node y en el mismo proyecto que el código que falla:
node -p "process.versions.undici"
npm ls undiciEl primero imprimió 8.12.2 en Node 26.13.0 y 7.30.1 en Node 24.22.2. En un proyecto de prueba con su propio undici 8 y cheerio@1.0.0, el segundo imprimió:
my-app@1.0.0 /lab/npm-ls-demo/my-app
├─┬ cheerio@1.0.0
│ └── undici@6.29.1
└── undici@8.12.4El undici@8.12.4 de nivel superior es la copia propia del proyecto. cheerio@1.0.0 trae su propio undici@6.29.1. Una entrada 5.x o 6.x es una sospechosa, no una prueba: solo falla si un dispatcher creado por ella llega al fetch integrado. La traza lo aclara, porque su primera línea at muestra la ruta de la copia que lanzó el error. Una herramienta que compila undici dentro de un único archivo no aparece en npm ls; la traza de Corepack en corepack#834 apunta dentro de corepack.cjs.
Soluciones A y B: fetch y dispatcher del mismo undici
Solución A: usa el fetch del propio undici
Importa fetch del mismo paquete que el dispatcher, para que las dos mitades vengan siempre de una sola versión:
import { fetch, ProxyAgent } from 'undici';
const dispatcher = new ProxyAgent(process.env.HTTPS_PROXY);
const res = await fetch('https://example.com/', { dispatcher });
console.log(res.status);En el laboratorio funcionó con undici 5.29.0, 6.29.1, 7.30.2 y 8.12.4 tanto en Node 26.13.0 como en 24.22.2, con Agent y ProxyAgent, y con EnvHttpProxyAgent cuando la versión lo tiene; cada petición por proxy llegó al proxy. Úsala en bibliotecas, CLI y todo lo que se ejecute en más de una línea de Node. Toma FormData y las demás clases de cuerpo del mismo paquete que fetch (documentación de undici). Las correcciones de Serverless y career-ops y el pull request abierto de Vercel CLI enlazados abajo siguen este camino.
Solución B: la misma versión mayor que process.versions.undici
npm install undici@8Con undici 8.12.4, el fetch integrado de Node 26.13.0 aceptó Agent, ProxyAgent y EnvHttpProxyAgent. En Node 24.22.2 los mismos dispatchers fallaron con invalid onRequestStart method, así que esta solución ata tu código a una sola línea de Node. undici 8 necesita Node.js 22.19.0 o posterior (guía de migración). undici 7.30.2 funcionó en las dos líneas en el laboratorio, y Redocly CLI y el cliente de Qdrant pasaron a undici 7; es una foto de dos versiones de Node, no una promesa sobre la siguiente.
Detrás de un proxy: ProxyAgent, EnvHttpProxyAgent, setGlobalDispatcher
ProxyAgent y EnvHttpProxyAgent de undici 5 o 6 fallan exactamente igual que un Agent normal. Añadir NODE_USE_ENV_PROXY=1 no ayuda mientras tu código siga pasando el dispatcher antiguo: un ProxyAgent de undici 6.29.1 falló con invalid onError method en Node 26.13.0 con la variable definida.
setGlobalDispatcher() falla de forma más silenciosa. En Node 26.13.0, un ProxyAgent global de undici 5.29.0, 6.29.1 o de las versiones 7.x que probamos anteriores a 7.27.0 (de 7.0.0 a 7.26.0), y un EnvHttpProxyAgent global de 6.29.1, no lanzaron ningún error. El fetch integrado los ignoró y se conectó directamente. Contra https://example.com, un ProxyAgent global de undici 6.29.1 obtuvo un 200 mientras el proxy no registraba nada, así que la petición salió desde nuestra propia IP; en Node 24.22.2 el mismo script pasó por el proxy. undici 7.27.0, 7.28.0, 7.30.2 y 8.12.4 funcionaron de forma global. Ninguna de las versiones antiguas escribe Symbol.for('undici.globalDispatcher.2'), donde undici 8 guarda su dispatcher global; 7.27.0 y posteriores sí.
Solución C: deja que Node lea las variables de proxy
Si el dispatcher solo existe para enviar el tráfico por HTTPS_PROXY, elimínalo y deja que lo haga Node:
export HTTPS_PROXY=http://USER:PASS@proxy.example:8080
export NO_PROXY=localhost,127.0.0.1
node --use-env-proxy app.mjsNODE_USE_ENV_PROXY=1, el flag --use-env-proxy y NODE_OPTIONS=--use-env-proxy funcionaron en Node 26.13.0 y 24.22.2, para fetch y para https.get. La opción llegó primero para fetch, en este commit de Node; la documentación de Node recoge NODE_USE_ENV_PROXY desde 24.0.0 y --use-env-proxy desde 24.5.0, y ambos desde 22.21.0. Lo que mostró el laboratorio:
- Solo se lee al arrancar. Definir
process.env.NODE_USE_ENV_PROXY = '1'dentro del script no hizo nada. Para activarlo en tiempo de ejecución, llama ahttp.setGlobalProxyFromEnv()(añadido en 24.14.0 y 25.4.0); envió por el proxy tantofetchcomohttps.get. - Solo HTTP_PROXY. Con solo
HTTP_PROXYdefinido,fetchlo usó para una URLhttps://, perohttps.getse conectó directamente. DefineHTTPS_PROXY. - NO_PROXY.
target.test,.test,*.test,*,target.test:8443yno_proxyen minúsculas evitaron el proxy;target.test:9999no, porque el puerto es distinto. SinNO_PROXY, las peticiones alocalhosty127.0.0.1pasaron por el proxy;NO_PROXY=localhost,127.0.0.1las envió directas.
El fetch de Node 26 negoció HTTP/2 con el destino del laboratorio, también dentro del túnel CONNECT, mientras que Node 24 usó HTTP/1.1. El proxy vio la misma línea CONNECT en ambos casos, y ninguna petición falló por ello.
Vercel CLI, Corepack, Redocly y otras herramientas que no controlas
Comprobado el 7 de noviembre de 2026:
- Vercel CLI. vercel#17629 está abierto: con cualquier variable de proxy definida, las versiones 59.18.0 y 59.23.1 fallan en Node 26.8.2 con
invalid onError methoddesde un undici 5.29.0 incluido y funcionan en Node 24.21.0. La corrección, vercel#17634, no está fusionada. Hasta que una versión la incluya, ejecuta el CLI con Node 24. - Corepack. corepack#834 informa de un
ProxyAgentde undici 6 incluido que falla en Node 26.0.0 y 26.1.0. Corepack 0.35.0 abandonó undici y usa el soporte de proxy de Node, así que defineNODE_USE_ENV_PROXY=1; quien abrió el issue confirmó que esa combinación funciona. El archivo de Node 26.13.0 que instalamos no trae el binariocorepack(el de 24.22.2 sí), así que comprueba qué versión instalaste concorepack --version. - Redocly CLI. redocly-cli#3232 cubre
login,pushy los demás comandos de Reunite. La 2.62.2 los corrigió en Node 26 y pasó a undici 7.30.0. - Serverless Framework. La 4.44.0 corrigió la descarga por proxy del paquete npm en Node 26 (#13918), y la 4.45.0 corrigió sus peticiones a la API por proxy en Node 26.
- Bibliotecas cliente, sin proxy de por medio. SocratiCode#154:
@qdrant/js-client-restanterior a 1.19 pasa unAgentde undici 6 alfetchintegrado, y la 1.19.0 pasó a undici 7. almyty#702: unAgentcon fijación anti-SSRF de undici 6.27.0 falló en Node 26.9.0 y su lookup de DNS propio nunca se ejecutó; la corrección subió undici a ^8.11.0. career-ops#4808: la salida por proxy con unEnvHttpProxyAgentde undici 6 fallaba en Node 26; la v1.36.0 lo combina con elfetchdel propio undici, el mismo enfoque que el anterior #4787.
Para cualquier otra herramienta: actualízala, mientras tanto ejecútala con Node 24 e informa del error con su cause y la primera línea at.
Comprueba que de verdad pasa por el proxy
Una respuesta con estado 200 no demuestra que la haya transportado el proxy: un dispatcher global ignorado o una coincidencia de NO_PROXY envía la petición directa, y un sitio accesible sigue respondiendo 200. Mira primero el propio registro del proxy; en el laboratorio cada petición por proxy dejó una línea CONNECT con el host y el puerto, y las que lo evitaron no dejaron ninguna. Si no tienes acceso al registro del proxy, pregunta a un servicio que devuelva tu IP:
const res = await fetch('https://api.ipify.org?format=json');
console.log(await res.json());Ejecútalo una vez con tu configuración de proxy, por ejemplo con HTTPS_PROXY definido y node --use-env-proxy, y otra sin ella. Si las dos imprimen la misma IP, la petición rodeó el proxy.
Preguntas frecuentes
¿undici 7 provoca «invalid onError method» en Node.js 26?
No en nuestras pruebas. Agent y ProxyAgent de todas las versiones 7.x que probamos, de 7.0.0 a 7.30.2, funcionaron al pasarlos por petición, y también EnvHttpProxyAgent de 7.30.2. setGlobalDispatcher() de 7.x anterior a 7.27.0 se ignoró sin ningún error.
¿Por qué funciona en Node 24 y falla en Node 26?
Node 24.22.2 incluye undici 7.30.1, que aceptó dispatchers de undici 5 y 6. Node 26.13.0 incluye undici 8.12.2, que solo envía los callbacks nuevos.
¿Lo arregla NODE_USE_ENV_PROXY=1?
Solo después de eliminar el dispatcher antiguo. Con un ProxyAgent de undici 6 todavía pasado a fetch, Node 26 falló igual con la variable definida.
Veo «invalid onRequestStart method». ¿Es el mismo problema?
Sí, a la inversa: un dispatcher de undici 8 pasado al fetch de una línea de Node más antigua; lo vimos en Node 24.22.2. La solución A también funciona ahí.
Guías relacionadas
- Corregir TypeError: fetch failed de Node.js detrás de un proxy lee todas las causas de la cadena, no solo esta.
- Usar un proxy con fetch de Node.js configura un
ProxyAgentcon autenticación y un plazo máximo. - fetch de Node.js ignora HTTPS_PROXY explica el timeout que aparece sin la activación explícita.
- Variables de entorno de proxy compara cómo las leen curl, Requests y Node.
- Configuración de proxy en npm (en inglés) consigue que
npm install undicipase por un proxy.
Prueba la solución con una IP real de operador móvil. Un proxy móvil de ipvolt es un dispositivo dedicado en una red de operador, desde US$9 al día. Pon su URL en HTTPS_PROXY y ejecuta la comprobación de IP de arriba con --use-env-proxy.
Fuentes y lecturas adicionales
Referencias técnicas usadas para esta guía. Consulta la documentación de tu versión instalada y la configuración compatible de tu proveedor.
- undici: Migrating from undici 7 to 8 (handler callbacks, global dispatcher)
- undici: Undici module vs. Node.js built-in fetch
- Node.js CLI: --use-env-proxy
- Node.js CLI: NODE_USE_ENV_PROXY=1
- Node.js HTTP: Built-in proxy support and NO_PROXY format
- Node.js HTTP: http.setGlobalProxyFromEnv()
- Node.js commit ada34bd: proxy support for fetch under NODE_USE_ENV_PROXY
- vercel/vercel#17629: CLI fails on Node.js 26 with a proxy variable set
- vercel/vercel#17634: route the CLI through its bundled undici fetch
- nodejs/corepack#834: bundled undici 6 ProxyAgent on Node.js 26
- Corepack 0.35.0 release notes
- Redocly/redocly-cli#3232: Reunite commands fail on Node.js 26
- Redocly CLI 2.62.2 release notes
- serverless/serverless#13918: proxied binary downloads on Node.js 26
- Serverless Framework 4.44.0 release notes
- Serverless Framework 4.45.0 release notes
- giancarloerra/SocratiCode#154: Qdrant client before 1.19 on Node.js 26
- almyty-inc/almyty#702: SSRF-pinning agent and undici 8
- career-ops-hq/career-ops#4808: proxy egress fails on Node.js 26
- career-ops-hq/career-ops#4787: drive the proxy agent with undici's own fetch