This error occurs when Node.js cannot parse a URL string using the WHATWG URL standard. The URL constructor is strict and rejects malformed URLs, missing protocols, unencoded special characters, or incomplete URL structures. This is a common issue when building dynamic URLs or handling user input.
The "Invalid URL" error in Node.js indicates that the URL string passed to the URL constructor or related parsing methods does not conform to the WHATWG URL standard. Node.js uses strict URL parsing, which means any deviation from the standard URL format will throw this error. Common reasons include: - Missing or incomplete protocol (like "https://" or "http://") - Special characters that aren't properly percent-encoded - Invalid hostname or port values - Malformed URL structure This error is thrown by the constructor `new URL()` and the static method `URL.parse()`. Unlike the legacy `url.parse()` which is lenient, the WHATWG URL API is strict and designed for web standards compliance.
The most common cause is a missing protocol. Ensure your URL starts with a valid protocol:
// ❌ WRONG - Missing protocol
const url = new URL('example.com'); // Error: Invalid URL
// ❌ WRONG - Incomplete protocol
const url = new URL('https://'); // Error: Invalid URL
// ✅ CORRECT - Full protocol and hostname
const url = new URL('https://example.com');
console.log(url.hostname); // example.comSupported protocols: http:, https:, ftp:, file:, ws:, wss:
If you're working with relative URLs, use the second argument to provide a base:
// Working with relative URLs
const baseUrl = new URL('https://example.com/api/');
const relativeUrl = new URL('/path/to/resource', baseUrl);
console.log(relativeUrl.href); // https://example.com/path/to/resourceSpecial characters in URLs must be percent-encoded (space = %20, etc.):
// ❌ WRONG - Spaces not encoded
const url = new URL('https://example.com/search?q=hello world');
// ✅ CORRECT - Use encodeURIComponent for query parameters
const query = 'hello world';
const url = new URL(`https://example.com/search?q=${encodeURIComponent(query)}`);
// ✅ OR construct with URLSearchParams
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'hello world'); // Automatically encodedFor path components:
// ✅ Use encodeURIComponent for path segments
const filename = 'my document.pdf';
const url = new URL(`https://example.com/files/${encodeURIComponent(filename)}`);URLSearchParams handles encoding automatically:
const url = new URL('https://example.com/api');
url.searchParams.append('name', 'John Doe'); // Space encoded automatically
url.searchParams.append('tags', 'a,b,c'); // Comma encoded automatically
console.log(url.href);
// https://example.com/api?name=John+Doe&tags=a%2Cb%2CcWhen building URLs dynamically, validate before parsing:
// ✅ Build and validate safely
function buildUrl(protocol, host, path, params) {
try {
// Construct the base URL
const url = new URL(`${protocol}://${host}`);
// Add path
url.pathname = path;
// Add query parameters safely
Object.entries(params).forEach(([key, value]) => {
url.searchParams.set(key, String(value));
});
return url;
} catch (error) {
console.error('Invalid URL construction:', error.message);
return null;
}
}
const url = buildUrl('https', 'api.example.com', '/users', {
name: 'John Doe',
role: 'admin'
});
if (url) {
console.log(url.href);
}Validate user input before using in URLs:
function isValidHostname(hostname) {
// Simple validation - customize based on your needs
return /^[a-zA-Z0-9.-]+$/.test(hostname);
}
function buildUserUrl(userInput) {
if (!isValidHostname(userInput)) {
throw new Error('Invalid hostname format');
}
return new URL(`https://${userInput}`);
}
try {
const url = buildUserUrl(userInput);
} catch (error) {
console.error('Failed to build URL:', error.message);
}Wrap URL parsing in error handling to gracefully handle invalid inputs:
// ✅ Proper error handling
function parseUrl(urlString) {
try {
const url = new URL(urlString);
return { success: true, url };
} catch (error) {
if (error.code === 'ERR_INVALID_URL') {
return {
success: false,
error: 'Invalid URL format',
input: urlString
};
}
throw error;
}
}
const result = parseUrl(userInput);
if (!result.success) {
console.error(result.error);
} else {
console.log('Parsed URL:', result.url.href);
}Validate before parsing in API routes:
// Express/Node.js HTTP example
app.get('/fetch-url', (req, res) => {
const { url } = req.query;
if (!url || typeof url !== 'string') {
return res.status(400).json({ error: 'Missing or invalid url parameter' });
}
try {
const parsed = new URL(url);
// Safe to use parsed URL
return res.json({ success: true, url: parsed.href });
} catch (error) {
return res.status(400).json({
error: 'Invalid URL format',
message: error.message
});
}
});In Node.js 19.9.0+, use URL.canParse() to check if a URL is valid:
// ✅ Check if URL is parseable first (Node.js 19.9.0+)
const urlString = 'https://example.com/path?query=value';
if (URL.canParse(urlString)) {
const url = new URL(urlString);
console.log('Valid URL:', url.href);
} else {
console.error('Invalid URL format');
}Inline validation in conditionals:
function processUrl(input) {
// Only parse if it's valid
if (URL.canParse(input)) {
const url = new URL(input);
return {
hostname: url.hostname,
pathname: url.pathname,
search: url.search
};
}
return null;
}Fallback for older Node.js versions:
function isValidUrl(urlString) {
// For Node.js < 19.9.0
if (typeof URL.canParse === 'function') {
return URL.canParse(urlString);
}
// Fallback: use try-catch
try {
new URL(urlString);
return true;
} catch {
return false;
}
}
if (isValidUrl(userInput)) {
const url = new URL(userInput);
}URLSearchParams automatically handles encoding, preventing URL parsing errors:
// ✅ Safe query parameter construction
const url = new URL('https://api.example.com/search');
// Add parameters (automatically encoded)
const params = new URLSearchParams({
q: 'hello world',
filter: 'recent',
tags: 'javascript, node.js'
});
url.search = params.toString();
console.log(url.href);
// https://api.example.com/search?q=hello+world&filter=recent&tags=javascript%2C+node.js
// Or use searchParams property directly
url.searchParams.set('page', '1');
url.searchParams.set('limit', '10');Building complex query strings:
function buildApiUrl(endpoint, filters) {
const url = new URL(`https://api.example.com${endpoint}`);
Object.entries(filters).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
// Handle arrays
if (Array.isArray(value)) {
value.forEach(v => url.searchParams.append(key, v));
} else {
url.searchParams.set(key, String(value));
}
}
});
return url.href;
}
const apiUrl = buildApiUrl('/users', {
name: 'John Doe',
roles: ['admin', 'user'],
active: true
});
console.log(apiUrl);WHATWG URL Standard vs Legacy url.parse()
Node.js has two URL parsing APIs:
// Legacy (lenient, non-standard)
const legacyUrl = require('url').parse('https://example.com');
// More forgiving, but not recommended for new code
// WHATWG (strict, web standard)
const url = new URL('https://example.com');
// Strict parsing, better for securityUse the WHATWG new URL() for all new code. The legacy parser is lenient but prone to security issues.
Internationalized Domain Names (IDN)
URLs with non-ASCII characters in hostnames must be Punycode-encoded:
// ✅ Use Punycode for international domains
const idnUrl = new URL('https://münchen.de');
console.log(idnUrl.hostname); // xn--mnchen-3ya.de (Punycode)
// Most modern systems handle this automaticallyPort Validation
Invalid ports cause Invalid URL errors:
// ❌ Invalid port
const url = new URL('https://example.com:99999'); // Error: Invalid URL
// ✅ Valid ports
const url = new URL('https://example.com:8080');
const url2 = new URL('https://example.com:443'); // https defaultFile URLs
Be careful with file:// URLs - they have special rules:
// ✅ Valid file URLs
const fileUrl = new URL('file:///path/to/file.txt');
const fileUrl2 = new URL('file:///C:/Users/User/file.txt'); // Windows
// ❌ Invalid file URLs
const badFile = new URL('file://path/to/file.txt'); // Missing third slashUsing URL in fetch() requests
URLs passed to fetch must be valid:
async function fetchData(urlString) {
try {
// Validate URL before fetching
const url = new URL(urlString);
const response = await fetch(url.href);
return response.json();
} catch (error) {
if (error.code === 'ERR_INVALID_URL') {
console.error('Invalid URL provided to fetch');
}
throw error;
}
}Debugging URL Parsing Issues
When debugging URL errors, log the exact input:
function debugUrl(urlString) {
console.log('Attempting to parse:', JSON.stringify(urlString));
console.log('Length:', urlString.length);
console.log('Starts with protocol:', /^[a-z][a-z0-9+.-]*:/.test(urlString));
try {
return new URL(urlString);
} catch (error) {
console.error('Parse failed:', error.message);
// Show character codes for hidden characters
console.error('Character codes:', Array.from(urlString).map(c => c.charCodeAt(0)));
throw error;
}
}URL with Authentication
Usernames and passwords in URLs must be properly formatted:
// ✅ Valid URL with credentials
const url = new URL('https://user:[email protected]');
// ✅ Special characters in credentials must be encoded
const username = 'user@domain';
const password = 'p@ss:word';
const url2 = new URL(`https://${encodeURIComponent(username)}:${encodeURIComponent(password)}@example.com`);Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams