payloadcms/payload · error · APIError

Operator handler "${handler.name}" returned an invalid opera

Error message

Operator handler "${handler.name}" returned an invalid operand transform for the "${resolvedOperator}" operator at path "${path}". Expected an object with "column" and "value" properties.

What it means

`APIError` from `buildOperatorConstraint`: a `transformOperands` handler returned a value that is not a plain object with both `column` and `value` properties. The contract requires the handler to return `{ column: Column|SQL, value: unknown }` so the next handler / the final comparison can consume it; anything else (undefined, array, missing key) breaks the pipeline.

Source

Thrown at packages/drizzle/src/queries/buildOperatorConstraint.ts:83

    resolvedOperator,
    storage: 'column',
    value: args.value,
  }

  for (const handler of matchingHandlers.filter(isTransformHandler)) {
    let result: { column: Column | SQL; value: unknown }

    try {
      result = handler.transformOperands({ ...context })
    } catch (error) {
      throw new Error(
        `Operator handler "${handler.name}" threw while transforming operands for the "${resolvedOperator}" operator at path "${path}".`,
        { cause: error },
      )
    }

    if (!result || typeof result !== 'object' || !('column' in result) || !('value' in result)) {
      throw new APIError(
        `Operator handler "${handler.name}" returned an invalid operand transform for the "${resolvedOperator}" operator at path "${path}". Expected an object with "column" and "value" properties.`,
      )
    }

    context.column = result.column
    context.value = result.value
  }

  const replacementHandler = matchingHandlers.find(isReplacementHandler)

  if (replacementHandler) {
    try {
      return replacementHandler.build({ ...context })
    } catch (error) {
      throw new Error(
        `Operator handler "${replacementHandler.name}" threw while building the "${resolvedOperator}" comparison at path "${path}".`,
        { cause: error },
      )

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Make the `transformOperands` implementation always return `{ column, value }` on every code path, including early returns.
  2. Add a unit test asserting the handler's return shape for each operator/field type it matches.
  3. If the transform shouldn't apply for some input, return `{ column, value }` unchanged rather than nothing.

Example fix

// before
transformOperands: ({ column, value }) => {
  if (value === null) return // returns undefined -> APIError
  return { column: sql`lower(${column})`, value: String(value).toLowerCase() }
}
// after
transformOperands: ({ column, value }) => {
  if (value === null) return { column, value }
  return { column: sql`lower(${column})`, value: String(value).toLowerCase() }
}
Defensive patterns

Strategy: validation

Validate before calling

// Runtime check at handler registration
for (const h of operatorHandlers ?? []) {
  if (typeof h.transformOperands === 'function') {
    const out = h.transformOperands({ column: {} as any, value: 'x' } as any)
    const ok = out && typeof out === 'object' && 'column' in out && 'value' in out
    if (!ok) throw new Error(`Handler ${h.name} returned invalid transform shape`)
  }
}

Type guard

const isValidTransformResult = (r: unknown): r is { column: unknown; value: unknown } =>
  typeof r === 'object' && r !== null && 'column' in r && 'value' in r

Try / catch

try {
  await payload.find({ collection, where })
} catch (err) {
  if (/invalid operand transform/.test(String(err?.message))) {
    payload.logger.error(`Fix the named handler to return { column, value } on every path.`)
  }
  throw err
}

Prevention

When it happens

Trigger: A custom/built-in operand-transform handler returns `undefined`, returns an object missing `column` or `value`, returns a non-object, or forgets a `return` statement. Triggered when a query matches that handler's `operators`/`fieldTypes`.

Common situations: Handler author forgot `return` in an early-out branch; returned only `value` or only `column`; returned a raw SQL fragment instead of the wrapper object.

Related errors


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