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
- Normalize input by prepending https:// when the scheme is missing before calling the API
- Validate the URL client-side with `new URL()` and reject failures early
- 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
- Trim and normalize user input (add https:// if scheme missing)
- Use `new URL()` as a client-side pre-check before sending
- Encode the URL parameter in the query string
- Reject empty/relative strings at the form level
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
- INVALID_PROTOCOL
- BLOCKED_URL
- Failed to load server config
- getDisplayMedia not supported
- No screen access granted
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/c97a677a153aff6b.
Report an issue: GitHub.