payloadcms/payload · error · APIError

Invalid field path.

Error message

Invalid field path.

What it means

getSelect builds a Payload select object from dot-notation field paths. Before traversing, it rejects any path whose segments include __proto__, constructor, or prototype (hasUnsupportedFieldPathSegment). This is a prototype-pollution guard; the 400 is intentional and security-relevant.

Source

Thrown at packages/plugin-import-export/src/utilities/getSelect.ts:24

const createSelect = (): SelectIncludeType => Object.create(null) as SelectIncludeType

/**
 * Takes an input of array of string paths in dot notation and returns a select object.
 * Used for both export and import to build Payload's select query format.
 *
 * @example
 * getSelect(['id', 'title', 'group.value', 'createdAt', 'updatedAt'])
 * // Returns: { id: true, title: true, group: { value: true }, createdAt: true, updatedAt: true }
 */
export const getSelect = (fields: string[]): SelectIncludeType => {
  const select = createSelect()

  fields.forEach((field) => {
    const segments = field.split('.')

    if (hasUnsupportedFieldPathSegment(segments)) {
      throw new APIError('Invalid field path.', 400, null, true)
    }

    let selectRef = select

    segments.forEach((segment, i) => {
      if (i === segments.length - 1) {
        selectRef[segment] = true
      } else {
        if (!Object.prototype.hasOwnProperty.call(selectRef, segment)) {
          selectRef[segment] = createSelect()
        }
        selectRef = selectRef[segment] as SelectIncludeType
      }
    })
  })

  return select
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Sanitize field paths: strip/reject __proto__, constructor, prototype before calling getSelect.
  2. Do not pass untrusted user input directly as the fields array; allowlist field names.
  3. Validate paths against the collection's known field names.

Example fix

// before
const select = getSelect(userSuppliedFields)
// after
const SAFE = /__proto__|constructor|prototype/
const safeFields = userSuppliedFields.filter((f) => !SAFE.test(f))
const select = getSelect(safeFields)
Defensive patterns

Strategy: validation

Validate before calling

const UNSUPPORTED = new Set(['__proto__', 'constructor', 'prototype'])
function sanitizeFields(fields: string[]): string[] {
  return fields.filter((f) => !f.split('.').some((seg) => UNSUPPORTED.has(seg)))
}
const select = getSelect(sanitizeFields(userFields))

Type guard

const isSafeFieldPath = (path: string): boolean =>
  !path.split('.').some((seg) => UNSUPPORTED.has(seg))

Prevention

When it happens

Trigger: A field path contains a dangerous segment, e.g. '__proto__.x', 'constructor.prototype', or a CSV/export field list sourced from untrusted input that includes such keys.

Common situations: Export/import field selection driven by user-supplied query params; CSV headers that happen to be '__proto__'; mapping table built from external data.

Related errors


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