Cómo solucionar ERR_INVALID_URL en Node.js
El error ERR_INVALID_URL en Node.js se produce cuando el constructor URL o url.parse() recibe una cadena que no es una URL válida. Las causas habituales son la falta de protocolo, caracteres especiales sin codificar y una codificación porcentual incorrecta. Soluciónalo validando la entrada y codificando los caracteres especiales antes de analizarla.
¿Qué causa ERR_INVALID_URL?
El error ERR_INVALID_URL (formalmente TypeError [ERR_INVALID_URL]: Invalid URL) lo lanza Node.js cuando el constructor URL o la función heredada url.parse() recibe una cadena que no se puede analizar como una URL válida. Este error es habitual al procesar entradas del usuario, analizar archivos de configuración o manejar URLs procedentes de fuentes externas.
El constructor URL sigue el estándar WHATWG URL, que es más estricto que el antiguo análisis basado en RFC. Una cadena debe incluir un esquema válido (como https:), una autoridad válida (nombre de host) y componentes de ruta y consulta con el formato correcto para ser aceptada como una URL válida.
// Esto lanza ERR_INVALID_URL
try {
const url = new URL('not-a-url');
} catch (err) {
console.log(err.code); // 'ERR_INVALID_URL'
console.log(err.message); // 'Invalid URL: not-a-url'
console.log(err.input); // 'not-a-url'
}
Causas habituales y soluciones
Causa 1: Falta el protocolo/esquema. El constructor URL requiere un esquema como https:// o http://. Cadenas como example.com/path o www.example.com fallarán.
// FALLA: sin protocolo
new URL('example.com/path'); // ERR_INVALID_URL
// SOLUCIÓN: añadir el protocolo
new URL('https://example.com/path'); // ¡Funciona!
// SOLUCIÓN: añadir el protocolo si falta
function ensureProtocol(urlString) {
if (!/^https?:\/\//i.test(urlString)) {
return 'https://' + urlString;
}
return urlString;
}
new URL(ensureProtocol('example.com/path')); // ¡Funciona!
Causa 2: Caracteres especiales sin codificar. Caracteres como espacios, llaves, barras verticales y ciertos caracteres Unicode no están permitidos en las URLs sin codificación.
// FALLA: espacios y caracteres especiales sin codificar
new URL('https://example.com/my file.pdf'); // ERR_INVALID_URL
new URL('https://example.com/path?q=a b'); // Puede fallar en algunas versiones
// SOLUCIÓN: codificar las partes problemáticas
const filename = encodeURIComponent('my file.pdf');
new URL('https://example.com/' + filename); // ¡Funciona!
// SOLUCIÓN: usar encodeURI para una URL completa con espacios
const rawUrl = 'https://example.com/my file.pdf';
new URL(encodeURI(rawUrl)); // ¡Funciona!
Causa 3: Codificación porcentual incorrecta. Si una URL contiene un signo de porcentaje que no va seguido de exactamente dos dígitos hexadecimales, el analizador la rechaza.
// FALLA: codificación porcentual incorrecta
new URL('https://example.com/100%done'); // ERR_INVALID_URL
new URL('https://example.com/path?q=50%'); // ERR_INVALID_URL
// SOLUCIÓN: codificar el signo de porcentaje suelto como %25
function fixPercentSigns(urlString) {
return urlString.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
}
new URL(fixPercentSigns('https://example.com/100%done'));
// ¡Funciona! La URL pasa a ser https://example.com/100%25done
Causa 4: Entrada vacía o nula. Pasar una cadena vacía, null o undefined al constructor URL también provoca este error.
// FALLA: entrada vacía o nula
new URL(''); // ERR_INVALID_URL
new URL(null); // ERR_INVALID_URL
new URL(undefined); // ERR_INVALID_URL
// SOLUCIÓN: validar la entrada antes de analizarla
function parseUrl(input) {
if (!input || typeof input !== 'string') {
return null;
}
try {
return new URL(input);
} catch {
return null;
}
}
Causa 5: URLs relativas sin una base. El constructor URL trata el primer argumento como una URL absoluta de forma predeterminada. Las URLs relativas como /path/to/page requieren una URL base como segundo argumento.
// FALLA: URL relativa sin base
new URL('/api/users'); // ERR_INVALID_URL
// SOLUCIÓN: proporcionar una URL base
new URL('/api/users', 'https://example.com');
// ¡Funciona! → https://example.com/api/users
// Patrón útil para clientes de API
const BASE_URL = 'https://api.example.com';
const endpoint = new URL('/v2/users?page=1', BASE_URL);
console.log(endpoint.href);
// "https://api.example.com/v2/users?page=1"
Cómo validar URLs antes de analizarlas
La forma más fiable de validar una URL en Node.js es usar el constructor URL dentro de un bloque try-catch. No existe un método integrado URL.isValid() (a fecha de Node.js 22), por lo que capturar el error es el enfoque habitual.
// Validador de URL sencillo
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch {
return false;
}
}
console.log(isValidUrl('https://example.com')); // true
console.log(isValidUrl('not-a-url')); // false
console.log(isValidUrl('')); // false
// Validador con protocolos permitidos
function isValidHttpUrl(string) {
try {
const url = new URL(string);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch {
return false;
}
}
console.log(isValidHttpUrl('https://example.com')); // true
console.log(isValidHttpUrl('ftp://example.com')); // false
console.log(isValidHttpUrl('javascript:alert(1)')); // false
Patrón seguro para analizar URLs
Aquí tienes un patrón robusto para analizar URLs que maneja todos los casos de error habituales y devuelve un objeto URL analizado o un error significativo.
class UrlParser {
static parse(input, base) {
// Validar la entrada
if (!input || typeof input !== 'string') {
return { ok: false, error: 'Input must be a non-empty string' };
}
// Eliminar espacios en blanco
const trimmed = input.trim();
// Corregir problemas habituales
let urlString = trimmed;
// Añadir el protocolo si falta
if (/^[a-zA-Z0-9]/.test(urlString) && !urlString.includes('://')) {
urlString = 'https://' + urlString;
}
// Corregir signos de porcentaje sueltos
urlString = urlString.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
try {
const url = base ? new URL(urlString, base) : new URL(urlString);
// Opcional: restringir a protocolos seguros
if (!['http:', 'https:'].includes(url.protocol)) {
return { ok: false, error: 'Unsupported protocol: ' + url.protocol };
}
return { ok: true, url };
} catch (err) {
return { ok: false, error: err.message };
}
}
}
// Uso
const result = UrlParser.parse('example.com/path?q=hello world');
if (result.ok) {
console.log(result.url.href);
} else {
console.error(result.error);
}
Buenas prácticas de prevención
Seguir estas prácticas te ayudará a evitar errores ERR_INVALID_URL en tus aplicaciones de Node.js y a construir un manejo de URLs más resistente.
- Envuelve siempre el análisis de URLs en try-catch. Nunca asumas que una cadena de URL es válida, especialmente cuando proviene de entradas del usuario, variables de entorno, bases de datos o APIs externas.
- Usa la API URL en lugar de concatenar cadenas. Construye las URLs con el constructor
URLyURLSearchParamsen vez de concatenar cadenas. La API se encarga de la codificación automáticamente. - Valida las variables de entorno al inicio. Si tu aplicación lee URLs de variables de entorno o archivos de configuración, valídalas al iniciar la aplicación, no la primera vez que se usan.
- Codifica la entrada del usuario antes de incrustarla en URLs. Usa siempre
encodeURIComponent()para los valores de los parámetros de consulta yencodeURI()para las URLs completas proporcionadas por los usuarios. - Usa un constructor de URLs para casos complejos. Para crear URLs con muchas partes dinámicas, crea una función auxiliar o una clase que maneje la codificación de forma coherente.
- Registra la entrada original en caso de error. Cuando captures un error
ERR_INVALID_URL, registra la entrada que lo provocó (saneada si pudiera contener datos sensibles) para ayudar a depurar el problema.
// Buena práctica: validar las URLs de configuración al inicio
const requiredUrls = ['API_BASE_URL', 'AUTH_SERVER_URL', 'WEBHOOK_URL'];
for (const envVar of requiredUrls) {
const value = process.env[envVar];
if (!value) {
throw new Error(envVar + ' environment variable is required');
}
try {
new URL(value);
} catch {
throw new Error(envVar + ' is not a valid URL: ' + value);
}
}
// Buena práctica: usar la API URL para construir URLs
function buildApiUrl(endpoint, params) {
const url = new URL(endpoint, process.env.API_BASE_URL);
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, String(value));
}
return url.toString();
}
const searchUrl = buildApiUrl('/api/search', {
q: 'Node.js & Express',
page: 1
});
// "https://api.example.com/api/search?q=Node.js+%26+Express&page=1"