medusajs/medusa · error · MedusaError

${message}

Error message

${message}

What it means

Thrown by the search-postgres module's filter compiler when it encounters a filter expression it cannot translate into SQL. The fail() helper wraps the message in a MedusaError of type NOT_ALLOWED, and it is reached from castScalar, comparisonSql, arrayContainsSql, and fieldPredicate when a filter uses an unsupported operator, an uncastable scalar value, or a field that cannot be predicated on.

Source

Thrown at packages/modules/providers/search-postgres/src/utils/filters.ts:6

import { SearchTypes } from "@medusajs/framework/types"
import { MedusaError } from "@medusajs/framework/utils"
import { IndexPlan, PlannedField } from "./plan"

function fail(message: string): never {
  throw new MedusaError(MedusaError.Types.NOT_ALLOWED, message)
}

export type SqlFragment = {
  sql: string
  params: unknown[]
}

/**
 * Builds a JSONB path expression against the `indexed` column for a dotted
 * field path. `variants.color` becomes `indexed->'variants'->'color'`.
 */
export function jsonbPath(path: string, asText = false): string {
  const segments = path.split(".")
  let expr = "indexed"

  for (let i = 0; i < segments.length; i++) {
    const segment = segments[i].replace(/'/g, "''")
    const isLast = i === segments.length - 1

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Check the exact field and operator in the filter that triggered the message and verify the field exists and is searchable in the index plan (see utils/plan.ts)
  2. Use an operator supported for that field kind, or cast the value to the field's declared type before sending
  3. Filter only on fields declared in your index settings / search settings for the index in question
  4. If you need unsupported semantics (e.g. full-text operators), move that logic out of the filter and into the query or post-filter in application code

Example fix

// before
await query.graph({
  entity: "product",
  filters: { title: { $lt: "abc" } }, // $lt unsupported on text
})
// after
await query.graph({
  entity: "product",
  filters: { title: { $like: "%abc%" } },
})
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED: Record<string, string[]> = {
  text: ["$eq", "$ne", "$like", "$in", "$nin"],
  number: ["$eq", "$ne", "$lt", "$lte", "$gt", "$gte", "$in"],
  boolean: ["$eq", "$ne"],
}

function validateFilter(field: string, kind: keyof typeof SUPPORTED, op: string) {
  if (!SUPPORTED[kind].includes(op)) {
    throw new Error(`Unsupported operator ${op} for ${kind} field ${field}`)
  }
}

Type guard

function isSupportedOperator(op: string, kind: string): boolean {
  const ops: Record<string, string[]> = {
    text: ["$eq", "$ne", "$like", "$in", "$nin"],
    number: ["$eq", "$ne", "$lt", "$lte", "$gt", "$gte", "$in"],
  }
  return (ops[kind] ?? []).includes(op)
}

Try / catch

try {
  await searchModule.search({ index, query, filters })
} catch (e) {
  if (e instanceof MedusaError && e.type === MedusaError.Types.NOT_ALLOWED) {
    // unsupported filter: log filter shape and fall back to broader search + app-side filtering
    logger.warn(`Unsupported filter: ${e.message}`)
  } else throw e
}

Prevention

When it happens

Trigger: Calling search with a filter whose operator is not supported for the field's kind (e.g. $lt on a text field), passing a scalar that cannot be cast to the field type (e.g. a string for a number field), using array containment on a non-array field, or filtering on a field missing from the index plan.

Common situations: Passing MeiliSearch-style filter syntax to the Postgres search provider, filtering on an attribute not marked searchable in the index settings, mismatched filter value types coming from loose JSON input, or upgrading search-postgres where supported operators changed.

Related errors


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