Debugging6 min di lettura

Come risolvere l'errore ERR_INVALID_URL in Node.js

L'errore ERR_INVALID_URL in Node.js si verifica quando il costruttore URL o url.parse() riceve una stringa che non è un URL valido. Le cause più comuni sono l'assenza del protocollo, i caratteri speciali non codificati e un percent-encoding malformato. Si risolve validando l'input e codificando i caratteri speciali prima del parsing.

Cosa causa l'errore ERR_INVALID_URL?

L'errore ERR_INVALID_URL (formalmente TypeError [ERR_INVALID_URL]: Invalid URL) viene generato da Node.js quando il costruttore URL o la funzione legacy url.parse() riceve una stringa che non può essere interpretata come un URL valido. Questo errore è frequente quando si elabora l'input dell'utente, si effettua il parsing di file di configurazione o si gestiscono URL provenienti da fonti esterne.

Il costruttore URL segue lo standard WHATWG URL, che è più rigoroso rispetto al vecchio parsing basato su RFC. Per essere accettata come URL valido, una stringa deve includere uno schema valido (come https:), un'authority valida (hostname) e componenti di percorso e query correttamente formattati.

// Questo genera 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'
}

Cause comuni e soluzioni

Causa 1: protocollo/schema mancante. Il costruttore URL richiede uno schema come https:// o http://. Stringhe come example.com/path o www.example.com falliranno.

// FALLISCE: nessun protocollo
new URL('example.com/path');  // ERR_INVALID_URL

// SOLUZIONE: aggiungere il protocollo
new URL('https://example.com/path');  // Funziona!

// SOLUZIONE: aggiungere il protocollo se mancante
function ensureProtocol(urlString) {
  if (!/^https?:\/\//i.test(urlString)) {
    return 'https://' + urlString;
  }
  return urlString;
}
new URL(ensureProtocol('example.com/path'));  // Funziona!

Causa 2: caratteri speciali non codificati. Caratteri come spazi, parentesi graffe, pipe e alcuni caratteri Unicode non sono ammessi negli URL senza codifica.

// FALLISCE: spazi e caratteri speciali non codificati
new URL('https://example.com/my file.pdf');    // ERR_INVALID_URL
new URL('https://example.com/path?q=a b');     // Può fallire in alcune versioni

// SOLUZIONE: codificare le parti problematiche
const filename = encodeURIComponent('my file.pdf');
new URL('https://example.com/' + filename);    // Funziona!

// SOLUZIONE: usare encodeURI per un URL completo con spazi
const rawUrl = 'https://example.com/my file.pdf';
new URL(encodeURI(rawUrl));  // Funziona!

Causa 3: percent-encoding malformato. Se un URL contiene un segno di percentuale che non è seguito esattamente da due cifre esadecimali, il parser lo rifiuta.

// FALLISCE: percent-encoding malformato
new URL('https://example.com/100%done');       // ERR_INVALID_URL
new URL('https://example.com/path?q=50%');     // ERR_INVALID_URL

// SOLUZIONE: codificare il segno di percentuale isolato come %25
function fixPercentSigns(urlString) {
  return urlString.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
}
new URL(fixPercentSigns('https://example.com/100%done'));
// Funziona! L'URL diventa https://example.com/100%25done

Causa 4: input vuoto o null. Anche passare una stringa vuota, null o undefined al costruttore URL provoca questo errore.

// FALLISCE: input vuoto o null
new URL('');           // ERR_INVALID_URL
new URL(null);         // ERR_INVALID_URL
new URL(undefined);    // ERR_INVALID_URL

// SOLUZIONE: validare l'input prima del parsing
function parseUrl(input) {
  if (!input || typeof input !== 'string') {
    return null;
  }
  try {
    return new URL(input);
  } catch {
    return null;
  }
}

Causa 5: URL relativi senza una base. Per impostazione predefinita, il costruttore URL tratta il primo argomento come un URL assoluto. Gli URL relativi come /path/to/page richiedono un URL di base come secondo argomento.

// FALLISCE: URL relativo senza base
new URL('/api/users');  // ERR_INVALID_URL

// SOLUZIONE: fornire un URL di base
new URL('/api/users', 'https://example.com');
// Funziona! → https://example.com/api/users

// Pattern utile per i client 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"

Come validare gli URL prima del parsing

Il modo più affidabile per validare un URL in Node.js è utilizzare il costruttore URL all'interno di un blocco try-catch. Non esiste un metodo integrato URL.isValid() (a partire da Node.js 22), quindi intercettare l'errore è l'approccio standard.

// Semplice validatore di 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

// Validatore con protocolli consentiti
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

Pattern per un parsing sicuro degli URL

Ecco un pattern robusto per il parsing degli URL che gestisce tutti i casi di errore più comuni e restituisce un oggetto URL analizzato oppure un errore significativo.

class UrlParser {
  static parse(input, base) {
    // Validare l'input
    if (!input || typeof input !== 'string') {
      return { ok: false, error: 'Input must be a non-empty string' };
    }

    // Rimuovere gli spazi iniziali e finali
    const trimmed = input.trim();

    // Correggere i problemi comuni
    let urlString = trimmed;

    // Aggiungere il protocollo se mancante
    if (/^[a-zA-Z0-9]/.test(urlString) && !urlString.includes('://')) {
      urlString = 'https://' + urlString;
    }

    // Correggere i segni di percentuale isolati
    urlString = urlString.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');

    try {
      const url = base ? new URL(urlString, base) : new URL(urlString);

      // Facoltativo: limitare ai protocolli sicuri
      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 };
    }
  }
}

// Utilizzo
const result = UrlParser.parse('example.com/path?q=hello world');
if (result.ok) {
  console.log(result.url.href);
} else {
  console.error(result.error);
}

Best practice di prevenzione

Seguire queste pratiche ti aiuterà a evitare gli errori ERR_INVALID_URL nelle tue applicazioni Node.js e a costruire una gestione degli URL più resiliente.

  • Racchiudi sempre il parsing degli URL in un try-catch. Non dare mai per scontato che una stringa URL sia valida, soprattutto quando proviene dall'input dell'utente, da variabili d'ambiente, da database o da API esterne.
  • Usa l'API URL invece della concatenazione di stringhe. Costruisci gli URL utilizzando il costruttore URL e URLSearchParams anziché concatenare stringhe. L'API gestisce automaticamente la codifica.
  • Valida le variabili d'ambiente all'avvio. Se la tua applicazione legge URL da variabili d'ambiente o file di configurazione, validali all'avvio dell'applicazione e non alla prima occasione in cui vengono utilizzati.
  • Codifica l'input dell'utente prima di inserirlo negli URL. Usa sempre encodeURIComponent() per i valori dei parametri di query e encodeURI() per gli URL completi forniti dagli utenti.
  • Usa un URL builder per i casi complessi. Per costruire URL con molte parti dinamiche, crea una funzione o una classe di supporto che gestisca la codifica in modo coerente.
  • Registra nei log l'input originale in caso di errore. Quando intercetti un errore ERR_INVALID_URL, registra nei log l'input che lo ha causato (opportunamente sanificato se potrebbe contenere dati sensibili) per facilitare il debug del problema.
// Buona pratica: validare gli URL di configurazione all'avvio
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);
  }
}

// Buona pratica: usare l'API URL per costruire gli 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"

Articoli correlati

Prova i nostri strumenti gratuiti