payloadcms/payload · error · Forbidden

You are not allowed to perform this action.

Error message

You are not allowed to perform this action.

What it means

Forbidden error (HTTP 403, 'You are not allowed to perform this action.') thrown at the very top of getFileFromURLHandler when req.user is falsy. The paste-URL endpoint requires an authenticated session even before collection/access checks run.

Source

Thrown at packages/payload/src/uploads/endpoints/getFileFromURL.ts:21

import { executeAccess } from '../../auth/executeAccess.js'
import { APIError } from '../../errors/APIError.js'
import { Forbidden } from '../../errors/Forbidden.js'
import { getRequestCollectionWithID } from '../../utilities/getRequestEntity.js'
import { isURLAllowed } from '../../utilities/isURLAllowed.js'
import { sanitizeFilename } from '../../utilities/sanitizeFilename.js'
import { safeFetch } from '../safeFetch.js'

// If doc id is provided, it means we are updating the doc
// /:collectionSlug/paste-url/:doc-id?src=:fileUrl

// If doc id is not provided, it means we are creating a new doc
// /:collectionSlug/paste-url?src=:fileUrl

export const getFileFromURLHandler: PayloadHandler = async (req) => {
  const { id, collection } = getRequestCollectionWithID(req, { optionalID: true })

  if (!req.user) {
    throw new Forbidden(req.t)
  }

  const config = collection?.config

  if (!config.upload?.pasteURL) {
    throw new APIError('Pasting from URL is not enabled for this collection.', 400)
  }

  if (id) {
    // updating doc
    const accessResult = await executeAccess({ slug: config.slug, req }, config.access.update)
    if (!accessResult) {
      throw new Forbidden(req.t)
    }
  } else {
    // creating doc
    const accessResult = await executeAccess({ slug: config.slug, req }, config.access?.create)
    if (!accessResult) {

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Authenticate before calling: include the Payload auth token (header Payload-Auth-Token or cookie) in the request.
  2. On the frontend, use the Payload client's fetch with credentials: 'include' or pass the token from the auth session.
  3. If the endpoint should be public, write a custom access function and a wrapper — but note the user check is unconditional here, so you'd need to override the endpoint.
  4. Handle 403 by redirecting the user to login, then retrying.

Example fix

// before
fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`)
// after — send auth token
fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `JWT ${token}` },
})
Defensive patterns

Strategy: validation

Validate before calling

function isAuthenticated(req: { user?: unknown }): boolean {
  return Boolean(req && req.user)
}
// before calling paste-url:
if (!isAuthenticated({ user: currentUser })) redirect('/login')

Type guard

const hasUser = (req: { user?: unknown }): req is { user: Record<string, unknown> } =>
  Boolean(req && typeof req === 'object' && 'user' in req && req.user)

Try / catch

try {
  await fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, { headers: { Authorization: `JWT ${token}` } })
} catch (e) {
  if (e.status === 403) redirect('/login?next=' + encodeURIComponent(location.pathname))
}

Prevention

When it happens

Trigger: Calling POST/GET /api/:collection/paste-url[/:doc-id]?src=<url> without a logged-in user — missing/invalid auth token, expired cookie, or no payload-auth strategy populated req.user.

Common situations: Frontend forgot to send the credentials/JWT; token expired; cookie not sent cross-origin (missing credentials: 'include'); custom auth strategy not setting req.user; testing the endpoint in a tool without auth headers.

Related errors


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