hcengineering/platform · error · LinkPreviewError

FETCH_FAILED

FETCH_FAILED

Error message

Failed to fetch URL: ${error instanceof Error ? error.message : 'Unknown error'}

What it means

LinkPreviewError with code FETCH_FAILED thrown by fetchWithTimeout for any fetch rejection that is not an AbortError — i.e. DNS failure, connection refused/reset, TLS errors, or other low-level network failures. The underlying error message is embedded in the message and attached as cause.

Source

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

// ============================================================================

async function fetchWithTimeout (url: string, options: RequestInit, timeoutMs: number): Promise<Response> {
  const controller = new AbortController()
  const timeoutId = setTimeout(() => {
    controller.abort()
  }, timeoutMs)

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    })
    return response
  } catch (error) {
    if (error instanceof Error && error.name === 'AbortError') {
      throw new LinkPreviewError(`Request timed out after ${timeoutMs}ms`, 'TIMEOUT', error)
    }
    throw new LinkPreviewError(
      `Failed to fetch URL: ${error instanceof Error ? error.message : 'Unknown error'}`,
      'FETCH_FAILED',
      error
    )
  } finally {
    clearTimeout(timeoutId)
  }
}

// ============================================================================
// oEmbed Functions
// ============================================================================

function findOEmbedProviderUrl (targetUrl: string): string | null {
  for (const provider of oembedProviders as OEmbedProvider[]) {
    const endpoints = provider.endpoints ?? []

    for (const endpoint of endpoints) {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Read error.cause/message to identify the underlying network failure (DNS vs refused vs TLS)
  2. Validate the URL is well-formed and reachable before enqueueing it for preview
  3. Fix DNS/proxy/TLS issues in the environment (check egress rules, trust the required CAs)
  4. Catch FETCH_FAILED and degrade gracefully (skip preview, show placeholder)
  5. Retry with backoff for transient connection resets

Example fix

// before
const { response } = await fetchWithTimeout(url, {}, timeout)
// after
try {
  const { response } = await fetchWithTimeout(url, {}, timeout)
} catch (e) {
  if (e instanceof LinkPreviewError && e.code === 'FETCH_FAILED') {
    console.error('network failure:', e.cause)
    return null
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isFetchableUrl(url: string): boolean {
  try { const u = new URL(url); return ['http:', 'https:'].includes(u.protocol) } catch { return false }
}

Type guard

function isFetchFailed(e: unknown): e is LinkPreviewError {
  return e instanceof LinkPreviewError && e.code === 'FETCH_FAILED'
}

Try / catch

try {
  await fetchWithTimeout(url, {}, timeoutMs)
} catch (e) {
  if (e instanceof LinkPreviewError && e.code === 'FETCH_FAILED') {
    console.error('fetch failed:', e.cause)
    return placeholderPreview(url)
  }
  throw e
}

Prevention

When it happens

Trigger: The fetch() call rejects with a non-abort error: unknown host (DNS NXDOMAIN), TCP connection refused, connection reset, TLS certificate failure, or invalid URL scheme passed to fetch.

Common situations: Previewing dead or mistyped URLs, environments without internet/egress access, self-signed or expired certificates on internal servers, IPv6 misconfiguration, or corporate proxies intercepting TLS.

Related errors


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