Как исправить ошибку ERR_INVALID_URL в Node.js
Ошибка ERR_INVALID_URL в Node.js возникает, когда конструктор URL или функция url.parse() получает строку, которая не является корректным URL. Частые причины: отсутствие протокола, незакодированные специальные символы и некорректное percent-encoding. Исправить её можно, проверяя входные данные и кодируя специальные символы перед разбором.
Что вызывает ошибку ERR_INVALID_URL?
Ошибка ERR_INVALID_URL (в полной форме TypeError [ERR_INVALID_URL]: Invalid URL) возникает в Node.js, когда конструктор URL или устаревшая функция url.parse() получает строку, которую невозможно разобрать как корректный URL. Эта ошибка часто встречается при обработке пользовательского ввода, разборе конфигурационных файлов или работе с URL из внешних источников.
Конструктор URL следует стандарту WHATWG URL Standard, который строже, чем более старый разбор на основе RFC. Чтобы строка была принята как корректный URL, она должна содержать допустимую схему (например, https:), допустимый authority (имя хоста), а также правильно оформленные компоненты пути и строки запроса.
// Это выбрасывает 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'
}
Частые причины и способы исправления
Причина 1: отсутствует протокол/схема. Конструктор URL требует наличия схемы, такой как https:// или http://. Строки вроде example.com/path или www.example.com завершатся ошибкой.
// ОШИБКА: нет протокола
new URL('example.com/path'); // ERR_INVALID_URL
// ИСПРАВЛЕНИЕ: добавьте протокол
new URL('https://example.com/path'); // Работает!
// ИСПРАВЛЕНИЕ: добавьте протокол, если он отсутствует
function ensureProtocol(urlString) {
if (!/^https?:\/\//i.test(urlString)) {
return 'https://' + urlString;
}
return urlString;
}
new URL(ensureProtocol('example.com/path')); // Работает!
Причина 2: незакодированные специальные символы. Символы, такие как пробелы, фигурные скобки, вертикальные черты и некоторые символы Unicode, не допускаются в URL без кодирования.
// ОШИБКА: незакодированные пробелы и специальные символы
new URL('https://example.com/my file.pdf'); // ERR_INVALID_URL
new URL('https://example.com/path?q=a b'); // Может завершиться ошибкой в некоторых версиях
// ИСПРАВЛЕНИЕ: закодируйте проблемные части
const filename = encodeURIComponent('my file.pdf');
new URL('https://example.com/' + filename); // Работает!
// ИСПРАВЛЕНИЕ: используйте encodeURI для полного URL с пробелами
const rawUrl = 'https://example.com/my file.pdf';
new URL(encodeURI(rawUrl)); // Работает!
Причина 3: некорректное percent-encoding. Если URL содержит знак процента, за которым не следуют ровно две шестнадцатеричные цифры, парсер отклонит его.
// ОШИБКА: некорректное percent-encoding
new URL('https://example.com/100%done'); // ERR_INVALID_URL
new URL('https://example.com/path?q=50%'); // ERR_INVALID_URL
// ИСПРАВЛЕНИЕ: закодируйте одиночный знак процента как %25
function fixPercentSigns(urlString) {
return urlString.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
}
new URL(fixPercentSigns('https://example.com/100%done'));
// Работает! URL становится https://example.com/100%25done
Причина 4: пустой или null-ввод. Передача пустой строки, null или undefined в конструктор URL также вызывает эту ошибку.
// ОШИБКА: пустой или null-ввод
new URL(''); // ERR_INVALID_URL
new URL(null); // ERR_INVALID_URL
new URL(undefined); // ERR_INVALID_URL
// ИСПРАВЛЕНИЕ: проверяйте входные данные перед разбором
function parseUrl(input) {
if (!input || typeof input !== 'string') {
return null;
}
try {
return new URL(input);
} catch {
return null;
}
}
Причина 5: относительные URL без базового адреса. По умолчанию конструктор URL трактует первый аргумент как абсолютный URL. Относительные URL, такие как /path/to/page, требуют указания базового URL в качестве второго аргумента.
// ОШИБКА: относительный URL без базового адреса
new URL('/api/users'); // ERR_INVALID_URL
// ИСПРАВЛЕНИЕ: укажите базовый URL
new URL('/api/users', 'https://example.com');
// Работает! → https://example.com/api/users
// Полезный паттерн для 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"
Как проверять URL перед разбором
Самый надёжный способ проверить URL в Node.js — использовать конструктор URL внутри блока try-catch. Встроенного метода URL.isValid() не существует (по состоянию на Node.js 22), поэтому перехват ошибки является стандартным подходом.
// Простой валидатор URL
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
// Валидатор с разрешёнными протоколами
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
Паттерн безопасного разбора URL
Ниже приведён надёжный паттерн разбора URL, который обрабатывает все распространённые случаи ошибок и возвращает разобранный объект URL или осмысленное сообщение об ошибке.
class UrlParser {
static parse(input, base) {
// Проверяем входные данные
if (!input || typeof input !== 'string') {
return { ok: false, error: 'Input must be a non-empty string' };
}
// Убираем пробельные символы
const trimmed = input.trim();
// Исправляем распространённые проблемы
let urlString = trimmed;
// Добавляем протокол, если он отсутствует
if (/^[a-zA-Z0-9]/.test(urlString) && !urlString.includes('://')) {
urlString = 'https://' + urlString;
}
// Исправляем одиночные знаки процента
urlString = urlString.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
try {
const url = base ? new URL(urlString, base) : new URL(urlString);
// Опционально: ограничиваем набор безопасными протоколами
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 };
}
}
}
// Использование
const result = UrlParser.parse('example.com/path?q=hello world');
if (result.ok) {
console.log(result.url.href);
} else {
console.error(result.error);
}
Рекомендации по предотвращению ошибок
Следование этим рекомендациям поможет вам избегать ошибок ERR_INVALID_URL в приложениях на Node.js и строить более устойчивую обработку URL.
- Всегда оборачивайте разбор URL в try-catch. Никогда не считайте строку URL корректной, особенно если она поступает из пользовательского ввода, переменных окружения, баз данных или внешних API.
- Используйте URL API вместо конкатенации строк. Стройте URL с помощью конструктора
URLиURLSearchParams, а не путём соединения строк. API выполняет кодирование автоматически. - Проверяйте переменные окружения при запуске. Если ваше приложение читает URL из переменных окружения или конфигурационных файлов, проверяйте их при запуске приложения, а не при первом использовании.
- Кодируйте пользовательский ввод перед встраиванием в URL. Всегда используйте
encodeURIComponent()для значений параметров запроса иencodeURI()для полных URL, предоставленных пользователями. - Используйте построитель URL для сложных случаев. Для построения URL с множеством динамических частей создайте вспомогательную функцию или класс, которые последовательно выполняют кодирование.
- Логируйте исходный ввод при ошибках. Перехватив ошибку
ERR_INVALID_URL, залогируйте вызвавший её ввод (очищенный от чувствительных данных, если они могут в нём присутствовать), чтобы упростить отладку.
// Хорошая практика: проверяйте URL из конфигурации при запуске
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);
}
}
// Хорошая практика: используйте URL API для построения URL
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"