payloadcms/payload · error · APIError
Failed to fetch the file from the provided URL.
Error message
Failed to fetch the file from the provided URL.
What it means
APIError thrown with the remote response's status code when response.ok is false after the redirect loop completes (non-2xx, non-3xx final response). The handler does not retry; it surfaces the upstream HTTP failure with the remote's status as the APIError status.
Source
Thrown at packages/payload/src/uploads/endpoints/getFileFromURL.ts:109
redirectCount++
if (redirectCount > maxRedirects) {
throw new APIError('Too many redirects.', 403)
}
const location = response.headers.get('location')
if (location) {
fileURL = new URL(location, fileURL).href
if (hasAllowList && !isURLAllowed(fileURL, config.upload.pasteURL.allowList)) {
throw new APIError('The provided URL is not allowed.', 400)
}
continue
}
}
break
}
if (!response.ok) {
throw new APIError('Failed to fetch the file from the provided URL.', response.status)
}
const rawFileName = decodeURIComponent(new URL(fileURL).pathname.split('/').pop() || '')
const safeFileName = sanitizeFilename(rawFileName)
const encodedFileName = encodeURIComponent(safeFileName).replace(
/['()]/g,
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
)
// Strip quotes, backslashes, and control chars from the ASCII fallback
const asciiFileName = safeFileName.replace(/["\\\r\n]/g, '_')
const headers: Record<string, string> = {
'Content-Disposition': `attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodedFileName}`,
'Content-Type': response.headers.get('content-type') || 'application/octet-stream',
}
const contentLength = response.headers.get('content-length')
if (contentLength) {View on GitHub (pinned to 00c58b35c0)
Solutions
- Verify the URL is reachable in a browser or via curl from the server's network.
- If the URL is time-limited (signed), refresh it before calling the endpoint.
- For 403/401, ensure the origin doesn't require headers/cookies the server can't provide — host the file elsewhere.
- Handle the APIError status in the client and prompt the user to re-check the URL.
Example fix
// client — surface the upstream status
try {
await fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, { method: 'POST', headers }).then((r) => {
if (!r.ok) throw new Error(`Remote returned ${r.status}`)
})
} catch (e) {
alert(`Could not fetch that URL (${e.message}). Check the link and try again.`)
} Defensive patterns
Strategy: try-catch
Validate before calling
async function remoteOk(u: string): Promise<boolean> {
try { const r = await fetch(u, { method: 'HEAD' }); return r.ok } catch { return false }
}
if (!(await remoteOk(src))) throw new Error('Source URL is not reachable') Type guard
const isOkStatus = (s: number): boolean => s >= 200 && s < 300
Try / catch
try {
await fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, { method: 'POST' })
} catch (e) {
if (/Failed to fetch the file/.test(e.message)) alert('Remote file unavailable; check the URL')
} Prevention
- HEAD/GET-check the URL from the server's network before submitting.
- Refresh expiring signed URLs before paste-URL calls.
- Avoid paste-URL for hosts requiring cookies/auth the server lacks.
- Surface the upstream status code to the user for actionable errors.
When it happens
Trigger: The remote src URL resolves to a final response with status >= 400 (or any non-2xx outside the redirect range), e.g. 404 Not Found, 403 Forbidden, 500 from the origin, or a 4xx from an expired signed URL. Also fires on 5xx server errors upstream.
Common situations: Linked file was deleted at the origin (404); the URL requires auth/cookies the server doesn't have (401/403); expired S3 signed URL; origin temporarily down (5xx); geo-blocked or rate-limited origin; wrong URL pasted by the user.
Related errors
- Too many redirects.
- You are not allowed to perform this action.
- Pasting from URL is not enabled for this collection.
- Request URL is missing.
- A valid URL string is required.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/d85d58a1eedeb9e5.
Report an issue: GitHub.