How to fix Error: Invalid URL (malformed URL string) in Node.js

Node.jsBEGINNERMEDIUMUpdated Aug 26, 2026· 186 views

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.

Quick fix

Fixes ERR_INVALID_URL

// ❌ 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.com
  1. Add the missing protocol to the URL
  2. Percent-encode special characters
  3. Handle dynamic URL construction safely
  4. Use try-catch to handle URL parsing errors
node.js — error
// ❌ 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.com
Error: Invalid URL (malformed URL string)

What this error means

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.

Typical symptoms

  • Error message: "TypeError [ERR_INVALID_URL]: Invalid URL"
  • Application crashes when parsing user-provided URLs
  • Dynamic URL construction fails with special characters
  • Incomplete URLs like "https://" without hostname cause errors
  • URLs with spaces or unencoded characters fail to parse
  • API requests with malformed URL parameters fail
  • URL query parameters with invalid formatting throw errors

Common causes

  1. Missing or incomplete protocol (http, https, ftp, file, etc.)
  2. Special characters not percent-encoded in URL components
  3. Spaces in URL strings instead of %20 encoding
  4. Invalid hostname or port numbers
  5. Attempting to parse relative URLs with the WHATWG API
  6. Malformed query strings with unescaped special characters
  7. Using the wrong URL parsing method for your use case
  8. Constructing URLs dynamically without proper validation

How to fix "Error: Invalid URL (malformed URL string)"

1Add the missing protocol to the URL

The most common cause is a missing protocol. Ensure your URL starts with a valid protocol:

javascript
// ❌ 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.com

Supported protocols: http:, https:, ftp:, file:, ws:, wss:

If you're working with relative URLs, use the second argument to provide a base:

javascript
// 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/resource
2Percent-encode special characters

Special characters in URLs must be percent-encoded (space = %20, etc.):

javascript
// ❌ 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 encoded

For path components:

javascript
// ✅ Use encodeURIComponent for path segments
const filename = 'my document.pdf';
const url = new URL(`https://example.com/files/${encodeURIComponent(filename)}`);

URLSearchParams handles encoding automatically:

javascript
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%2Cc
3Handle dynamic URL construction safely

When building URLs dynamically, validate before parsing:

javascript
// ✅ 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:

javascript
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);
}
4Use try-catch to handle URL parsing errors

Wrap URL parsing in error handling to gracefully handle invalid inputs:

javascript
// ✅ 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:

javascript
// 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
    });
  }
});
5Use URL.canParse() to validate before parsing

In Node.js 19.9.0+, use URL.canParse() to check if a URL is valid:

javascript
// ✅ 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:

javascript
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:

javascript
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);
}
6Use URLSearchParams for query string construction

URLSearchParams automatically handles encoding, preventing URL parsing errors:

javascript
// ✅ 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:

javascript
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);

Advanced notes

WHATWG URL Standard vs Legacy url.parse()

Node.js has two URL parsing APIs:

javascript
// 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 security

Use 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:

javascript
// ✅ 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 automatically

Port Validation

Invalid ports cause Invalid URL errors:

javascript
// ❌ 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 default

File URLs

Be careful with file:// URLs - they have special rules:

javascript
// ✅ 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 slash

Using URL in fetch() requests

URLs passed to fetch must be valid:

javascript
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:

javascript
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:

javascript
// ✅ Valid URL with credentials
const url = new URL('https://user:password@example.com');

// ✅ 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`);

Frequently asked questions

What does "ERR_INVALID_URL" mean?
js indicates that the URL string passed to the URL constructor or related parsing methods does not conform to the WHATWG URL standard. js uses strict URL parsing, which means any deviation from the standard URL format will throw this error. parse().
How do I fix "ERR_INVALID_URL"?
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.
What causes "ERR_INVALID_URL"?
Missing or incomplete protocol (http, https, ftp, file, etc.) Special characters not percent-encoded in URL components Spaces in URL strings instead of %20 encoding
Is the "ERR_INVALID_URL" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading