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
- Make the `transformOperands` implementation always return `{ column, value }` on every code path, including early returns.
- Add a unit test asserting the handler's return shape for each operator/field type it matches.
- 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
- Always return { column, value } from transformOperands, including early-out branches.
- Add a unit test asserting the return shape for each matched operator/field type.
- Use TypeScript return-type annotations to let the compiler enforce the shape.
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
- Operator handler "${handler.name}" threw while transforming
- Operator handler "${replacementHandler.name}" threw while bu
- Collection with the slug ${collectionSlug} was not found.
- Relationship field was not found
- Global with the slug ${globalSlug} was not found
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/33b80b9ccfad3f95.
Report an issue: GitHub.