{"record":{"id":"80321c8bb5943d01","repo":"medusajs/medusa","slug":"invalid-operator-operator","errorCode":null,"errorMessage":"Invalid operator: ${operator}","messagePattern":"Invalid operator: (.+?)","errorType":"validation","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/core/utils/src/dal/mikro-orm/cross-module-query/filter-sql.ts","lineNumber":388,"sourceCode":"      bindings.push(value as Knex.RawBinding)\n      return `${column} < ?`\n    case \"$lte\":\n      bindings.push(value as Knex.RawBinding)\n      return `${column} <= ?`\n    case \"$like\":\n      bindings.push(value as Knex.RawBinding)\n      return `${column} like ?`\n    case \"$ilike\":\n      bindings.push(value as Knex.RawBinding)\n      return `${column} ilike ?`\n    case \"$is\":\n      if (value === null) {\n        return `${column} is null`\n      }\n      bindings.push(value as Knex.RawBinding)\n      return `${column} is ?`\n    default:\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,\n        `Invalid operator: ${operator}`\n      )\n  }\n}\n\nfunction buildInArraySql(\n  column: string,\n  values: Knex.RawBinding[],\n  negated: boolean\n): SqlFragment | undefined {\n  if (!values.length) {\n    return negated ? undefined : { sql: \"1 = 0\", bindings: [] }\n  }\n\n  // Use `any(array[...])` so MikroORM's formatQuery inlines values as\n  // `array['a','b']` instead of the invalid `any('a','b')` from a single array\n  // binding with `any(?)`.","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/utils/src/dal/mikro-orm/cross-module-query/filter-sql.ts#L370-L406","documentation":"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.","triggerScenarios":"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.","commonSituations":"Porting Mongo-style filter syntax ($regex, $nor, $elemMatch) to Medusa's query API, or typos in dynamic filter builders.","solutions":["Replace the unsupported operator with a supported one ($like for substring, $gte already supported for numerics — check the FilterOperator union)","Build the filter from a whitelist of operators in your own code","Check the emitted SQL/log to confirm the operator mapping"],"exampleFix":"// before\nfilters: { title: { $regex: \"shirt\" } }\n// after\nfilters: { title: { $like: \"%shirt%\" } }","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['$eq','$ne','$gt','$gte','$lt','$lte','$in','$nin','$like','$ilike','$re','$contains','$startsWith','$endsWith','$null'])\nfor (const op of Object.keys(filter)) if (op.startsWith('$') && !ALLOWED.has(op)) throw new Error(`unsupported operator ${op}`)","typeGuard":"const isSupportedOperator = (op: string): boolean => !op.startsWith('$') || ALLOWED_OPS.has(op)","tryCatchPattern":"try { await query.graph(...) } catch (e) { if (/Invalid operator/.test(e.message)) translate filter and retry; else throw e }","preventionTips":["Build filters from a whitelist map, not raw user input","Prefer $like/$ilike over regex-style operators"],"tags":["query","filters","operators","remote-query"],"backgroundTag":"unsupported-query-operator","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}