hcengineering/platform · error · LinkPreviewError

INVALID_URL

INVALID_URL

Error message

Invalid URL: ${urlString}

What it means

validateUrl parses a URL string with `new URL()`. Strings that are not valid absolute URLs (including relative paths or malformed input) make the URL constructor throw, which is converted into LinkPreviewError with code INVALID_URL.

Source

Thrown at pods/link-preview/src/parse.ts:211

  if (ipType === 6) return isBlockedIpv6(host)

  // Some Node versions are stricter about IPv6 parsing. If it still looks like an IPv6 literal,
  // apply our IPv6 checks anyway (covers IPv6-mapped IPv4 forms like ::ffff:7f00:1).
  if (host.includes(':') && isBlockedIpv6(host)) return true

  // Hostname is not an IP literal. Keep legacy explicit localhost-ish blocks.
  // (We intentionally do not attempt DNS resolution here.)
  if (host.endsWith('.localhost')) return true

  return false
}

function validateUrl (urlString: string): URL {
  let url: URL
  try {
    url = new URL(urlString)
  } catch {
    throw new LinkPreviewError(`Invalid URL: ${urlString}`, 'INVALID_URL')
  }

  // Only allow HTTP(S) protocols
  if (!['http:', 'https:'].includes(url.protocol)) {
    throw new LinkPreviewError(
      `Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.`,
      'INVALID_PROTOCOL'
    )
  }

  // SSRF protection: block private/internal hosts and IP literals (incl. IPv6-mapped IPv4)
  if (isBlockedHost(url.hostname)) {
    throw new LinkPreviewError('Blocked URL: Access to internal addresses is not allowed.', 'BLOCKED_URL')
  }

  return url
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Normalize input by prepending https:// when the scheme is missing before calling the API
  2. Validate the URL client-side with `new URL()` and reject failures early
  3. Trim whitespace and encode the URL in the query string

Example fix

// before
validateUrl('example.com/page') // throws
// after
const raw = 'example.com/page'
validateUrl(raw.startsWith('http') ? raw : 'https://' + raw)
Defensive patterns

Strategy: validation

Validate before calling

function isAbsoluteHttpUrl(s: string): boolean {
  try { const u = new URL(s.trim()); return Boolean(u.hostname) } catch { return false }
}
if (!isAbsoluteHttpUrl(input)) throw new Error('provide an absolute http(s) URL')

Try / catch

try {
  return await fetchOEmbedData(client, url)
} catch (err) {
  if (err instanceof LinkPreviewError && err.code === 'INVALID_URL') {
    return await fetchOEmbedData(client, 'https://' + url)
  }
  throw err
}

Prevention

When it happens

Trigger: fetchOEmbedData / fetchWithValidatedRedirects / loadImageSize / parsedUrl called with '', 'not-a-url', 'example.com' (no scheme), or an unencoded/fragmented malformed string.

Common situations: User-typed input in a link field without normalization; missing protocol when pasting hostnames; stored bookmarks from an older schema; empty url query parameter.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/c97a677a153aff6b. Report an issue: GitHub.