payloadcms/payload · error · APIError
File URLs must use http or https.
Error message
File URLs must use http or https.
What it means
Thrown as a 400 when `source: 'externalURL'` is used and the parsed `URL.protocol` is anything other than `http:` or `https:`. The protocol allowlist is hard-coded to prevent SSRF via `file:`, `ftp:`, `data:`, and other schemes reaching internal fetchers.
Source
Thrown at packages/plugin-mcp/src/mcp/builtin/collections/fileInput.ts:99
file = {
name: sanitizeFilename(input.name),
data,
mimetype: input.mimeType,
size: data.length,
}
} else {
if (uploadConfig.pasteURL === false) {
throw new APIError(
`Uploading files from URLs is disabled for collection "${collectionSlug}".`,
400,
)
}
const url = new URL(input.url)
if (!['http:', 'https:'].includes(url.protocol)) {
throw new APIError('File URLs must use http or https.', 400)
}
if (
typeof uploadConfig.pasteURL === 'object' &&
!isURLAllowed(input.url, uploadConfig.pasteURL.allowList)
) {
throw new APIError('The provided file URL is not allowed.', 400)
}
file = await getExternalFile({
data: {
filename: sanitizeFilename(input.name || getURLFilename(url)),
url: input.url,
} as FileData,
req,
uploadConfig: {
...uploadConfig,
externalFileHeaderFilter: uploadConfig.externalFileHeaderFilter ?? (() => ({})),View on GitHub (pinned to 00c58b35c0)
Solutions
- Ensure the `url` value begins with `http://` or `https://`
- If the source object is only available via a non-http scheme, fetch it yourself and pass it as `source: 'base64'`
- Sanitize client-side input to reject non-http(s) schemes before calling the tool
Example fix
// before
{ source: 'externalURL', url: 'ftp://server/file.png' }
// after
{ source: 'externalURL', url: 'https://server/file.png' } Defensive patterns
Strategy: validation
Validate before calling
// Reject non-http(s) URLs before calling the tool
function assertHttpUrl(u: string) {
const parsed = new URL(u)
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
throw new Error(`URL must be http/https, got ${parsed.protocol}`)
} Type guard
function isHttpUrl(u: string): boolean {
try { const p = new URL(u).protocol; return p === 'http:' || p === 'https:' }
catch { return false }
} Try / catch
import { APIError } from 'payload'
try {
await tool.call({ source: 'externalURL', url })
} catch (e) {
if (e instanceof APIError && e.statusCode === 400 && /must use http or https/.test(e.message)) {
// prompt the user for a valid http(s) URL
}
throw e
} Prevention
- Validate URL scheme client-side with `new URL(...)` before submission
- If integrating with non-http sources (S3, FTP), fetch the bytes yourself and use base64
- Treat the scheme allowlist as an SSRF control — never widen it
When it happens
Trigger: Passing a `url` with scheme `ftp://`, `file://`, `data:`, `s3://`, etc. as an MCP tool's `externalURL` source; passing a URL without a scheme so `new URL(...)` yields an unexpected protocol.
Common situations: Client building the URL from user input that includes a `file://` path; copy-paste of an `s3://` object URI instead of its HTTPS access URL; malformed URL that defaults to an unexpected protocol.
Related errors
- Uploading files from URLs is disabled for collection "${coll
- The provided file URL is not allowed.
- Redirect target is not allowed.
- Blocked unsafe attempt to ${hostname}
- ${error.cause.message}
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/2fd6431843723bbd.
Report an issue: GitHub.