payloadcms/payload · error · PayloadSDKError

${message}

Error message

${message}

What it means

Thrown by the Payload SDK's request helper whenever the underlying `fetch` returns a non-OK status. It parses the response body for `errors`/`message` (falling back to `response.statusText`) and wraps them in a `PayloadSDKError` carrying `status`, `response`, and the structured `errors` array. This is the SDK's universal catch-all — the real cause is whatever the server returned (validation, auth, not-found, server error).

Source

Thrown at packages/sdk/src/index.ts:340

    if (!response.ok) {
      let errorData: {
        message?: string
      } & Partial<ErrorResult> = {}

      try {
        errorData = await response.json()
      } catch {
        // Response body may not be JSON
      }

      const errors: ErrorResult['errors'] = errorData.errors ?? [
        { message: errorData.message ?? response.statusText },
      ]

      const message = errors[0]?.message ?? response.statusText

      throw new PayloadSDKError({
        errors,
        message,
        response,
        status: response.status,
      })
    }

    return response
  }

  resetPassword<TSlug extends AuthCollectionSlug<T>>(
    options: ResetPasswordOptions<T, TSlug>,
    init?: RequestInit,
  ): Promise<ResetPasswordResult<T, TSlug>> {
    return resetPassword(this, options, init)
  }

  restoreGlobalVersion<TSlug extends GlobalSlug<T>>(

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Inspect `error.status` to route handling (401 → re-auth, 404 → not-found UX, 400 → show `error.errors`)
  2. Read `error.errors` (the structured array) for field-level validation messages
  3. If `error.message === response.statusText` and the body was non-JSON, a proxy/CDN likely intercepted — check the raw network response
  4. Reproduce the request with the same headers/cookie via curl to see the raw server response

Example fix

// before — unhandled rejection
await sdk.find({ collection: 'posts', where: { id: { equals: 'bad' } } })
// after — branch on status
try {
  await sdk.find({ collection: 'posts', where: { id: { equals: id } } })
} catch (e) {
  if (e instanceof PayloadSDKError) {
    if (e.status === 401) await relogin()
    else if (e.status === 400) showErrors(e.errors)
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate the call shape against the collection schema where possible
// (field types, required fields) to avoid the common 400 path before it hits the server

Type guard

import { PayloadSDKError } from '@payloadcms/sdk'
// or: function isPayloadSDKError(e: unknown): e is PayloadSDKError { return e instanceof PayloadSDKError }
function isPayloadSDKError(e: unknown): e is { status: number; errors: { message: string }[]; message: string } {
  return !!e && typeof (e as any).status === 'number' && Array.isArray((e as any).errors)
}

Try / catch

try {
  await sdk.find({ collection: 'posts', where })
} catch (e) {
  if (isPayloadSDKError(e)) {
    if (e.status === 401) await relogin()
    else if (e.status === 400) showFieldErrors(e.errors)
    else if (e.status === 404) showNotFound()
  }
  throw e
}

Prevention

When it happens

Trigger: Any SDK call (find, create, update, delete, auth) where the server responds with a non-2xx status — validation error (400), unauthorized (401), forbidden (403), not found (404), or server error (500). The body is JSON with `errors` or `message`, or non-JSON (then `statusText` is used).

Common situations: Sending invalid field values that fail server-side validation; expired/missing auth token; querying a non-existent collection or document ID; server threw during a hook; network proxy returning an HTML error page (non-JSON body).

Related errors


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