payloadcms/payload · warning · Error
Failed to fetch from ${stringifiedUrl}, ${error.message}
Error message
Failed to fetch from ${stringifiedUrl}, ${error.message} What it means
`safeFetch`'s fallback branch catches any fetch error that is **not** an SSRF `unsafe` block — network failures, DNS resolution errors (NXDOMAIN), TLS/cert errors, connection refused/reset, timeouts — and re-throws a plain `Error` `Failed to fetch from <stringifiedUrl>, <original message>`. The original cause is preserved in the message text (not in `error.cause`).
Source
Thrown at packages/payload/src/uploads/safeFetch.ts:112
redirect: 'manual', // Prevent automatic redirects
})) as unknown as Response
} catch (error) {
if (error instanceof Error) {
if (error.cause instanceof Error && error.cause.message.includes('unsafe')) {
// Errors thrown from within interceptors always have 'fetch error' as the message
// The desired message we want to bubble up is in the cause
throw new Error(error.cause.message)
} else {
let stringifiedUrl: string | undefined = undefined
if (typeof unverifiedUrl === 'string') {
stringifiedUrl = unverifiedUrl
} else if (unverifiedUrl instanceof URL) {
stringifiedUrl = unverifiedUrl.toString()
} else if (unverifiedUrl instanceof Request) {
stringifiedUrl = unverifiedUrl.url
}
throw new Error(`Failed to fetch from ${stringifiedUrl}, ${error.message}`)
}
}
throw error
}
}
View on GitHub (pinned to 00c58b35c0)
Solutions
- From the server, run `curl -v <url>` / `nslookup <hostname>` to confirm reachability and TLS.
- Open the required egress (host/port) in firewall/security groups; configure an HTTP(S) proxy if your network mandates one.
- Fix the target's TLS cert (valid, non-expired, trusted CA) or add the CA to the trust store.
- Use a resolvable, reachable hostname; avoid internal-only names in production fetches.
- Retry transient network failures with bounded backoff.
- Add the URL to `skipSafeFetch`/`allowList` only if you have deliberately bypassed safeFetch and still see transport errors.
Example fix
// before — calling update with an unreachable remote URL
await payload.update({ collection: 'media', id, data: { url: 'https://files.example.com/a.png' } })
// server cannot resolve/connect -> Failed to fetch from ...
// after — verify reachability first, then store a working URL
const ok = await fetch('https://files.example.com/a.png').then(r => r.ok).catch(() => false)
if (ok) {
await payload.update({ collection: 'media', id, data: { url: 'https://files.example.com/a.png' } })
} else {
// upload the bytes directly instead of remote-fetching
} Defensive patterns
Strategy: retry
Validate before calling
async function isReachable(url: string, timeoutMs = 5000): Promise<boolean> {
const ctrl = new AbortController()
const t = setTimeout(() => ctrl.abort(), timeoutMs)
try {
const res = await fetch(url, { method: 'HEAD', signal: ctrl.signal })
return res.ok
} catch { return false } finally { clearTimeout(t) }
}
if (!(await isReachable(doc.url))) {
// unreachable — fix DNS/TLS/egress or upload bytes directly
} Type guard
function isTransportError(err: unknown): boolean {
return err instanceof Error && /failed to fetch from/i.test(err.message) && !/unsafe/i.test(err.message)
} Try / catch
async function fetchWithRetry(url: string, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try { return await fetch(url) }
catch (err) {
if (i === attempts - 1 || !isTransportError(err)) throw err
await new Promise((r) => setTimeout(r, 2 ** i * 200))
}
}
} Prevention
- Verify egress (host/port), DNS, and TLS from the server before depending on remote fetch.
- Configure an HTTP(S) proxy if the network requires one.
- Use bounded backoff retry for transient transport errors.
- Keep target TLS certs valid and trusted.
When it happens
Trigger: An external-file or paste-URL fetch where: DNS for the hostname fails (NXDOMAIN), the connection is refused/reset, TLS handshake fails (expired/self-signed cert), the server times out, or a proxy/egress firewall blocks the connection. Distinct from a successful-but-non-2xx response (that's error 271).
Common situations: No/incorrect egress rules (firewall, security group, VPC). DNS misconfiguration in the container. Self-signed/expired TLS certs on the target. Target host down. A proxy is required but not configured. IPv6-only target with no IPv6 egress. Local dev hitting an unreachable hostname.
Related errors
- ${error.cause.message}
- Too many redirects (max ${maxRedirects})
- Failed to fetch file from ${fileURL}
- Blocked unsafe attempt to ${hostname}
- Failed to download: ${url}
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/bea0e8b5109b0c71.
Report an issue: GitHub.