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

  1. Ensure the `url` value begins with `http://` or `https://`
  2. If the source object is only available via a non-http scheme, fetch it yourself and pass it as `source: 'base64'`
  3. 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

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


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/2fd6431843723bbd. Report an issue: GitHub.