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 - 1View on GitHub (pinned to 5e06e544a2)
Solutions
- 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)
- Use an operator supported for that field kind, or cast the value to the field's declared type before sending
- Filter only on fields declared in your index settings / search settings for the index in question
- 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
- Validate filter operators against the field kind before calling search
- Only filter on fields declared searchable in index settings
- Add a thin wrapper around search calls that type-checks filter values
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.