medusajs/medusa · error · MedusaError

Invalid operator: ${operator}

Error message

Invalid operator: ${operator}

What it means

Part of Medusa's cross-module query builder that translates filter objects into SQL. Only a fixed set of operators ($eq, $ne, $lt, $gt, $in, $like, $null, etc.) is supported; anything else reaches the switch default and throws INVALID_DATA.

Source

Thrown at packages/core/utils/src/dal/mikro-orm/cross-module-query/filter-sql.ts:388

      bindings.push(value as Knex.RawBinding)
      return `${column} < ?`
    case "$lte":
      bindings.push(value as Knex.RawBinding)
      return `${column} <= ?`
    case "$like":
      bindings.push(value as Knex.RawBinding)
      return `${column} like ?`
    case "$ilike":
      bindings.push(value as Knex.RawBinding)
      return `${column} ilike ?`
    case "$is":
      if (value === null) {
        return `${column} is null`
      }
      bindings.push(value as Knex.RawBinding)
      return `${column} is ?`
    default:
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `Invalid operator: ${operator}`
      )
  }
}

function buildInArraySql(
  column: string,
  values: Knex.RawBinding[],
  negated: boolean
): SqlFragment | undefined {
  if (!values.length) {
    return negated ? undefined : { sql: "1 = 0", bindings: [] }
  }

  // Use `any(array[...])` so MikroORM's formatQuery inlines values as
  // `array['a','b']` instead of the invalid `any('a','b')` from a single array
  // binding with `any(?)`.

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Replace the unsupported operator with a supported one ($like for substring, $gte already supported for numerics — check the FilterOperator union)
  2. Build the filter from a whitelist of operators in your own code
  3. Check the emitted SQL/log to confirm the operator mapping

Example fix

// before
filters: { title: { $regex: "shirt" } }
// after
filters: { title: { $like: "%shirt%" } }
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = new Set(['$eq','$ne','$gt','$gte','$lt','$lte','$in','$nin','$like','$ilike','$re','$contains','$startsWith','$endsWith','$null'])
for (const op of Object.keys(filter)) if (op.startsWith('$') && !ALLOWED.has(op)) throw new Error(`unsupported operator ${op}`)

Type guard

const isSupportedOperator = (op: string): boolean => !op.startsWith('$') || ALLOWED_OPS.has(op)

Try / catch

try { await query.graph(...) } catch (e) { if (/Invalid operator/.test(e.message)) translate filter and retry; else throw e }

Prevention

When it happens

Trigger: Passing an unsupported operator in query filters, e.g. `{ price: { $gte: 10, $regex: "x" } }` or a typo like `$contains` where `$like` is expected, to remoteQuery/query.graph with cross-module SQL filters.

Common situations: Porting Mongo-style filter syntax ($regex, $nor, $elemMatch) to Medusa's query API, or typos in dynamic filter builders.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/80321c8bb5943d01. Report an issue: GitHub.