{"record":{"id":"2536160be439e49a","repo":"medusajs/medusa","slug":"message-253616","errorCode":null,"errorMessage":"${message}","messagePattern":"\\$\\{message\\}","errorType":"validation","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/modules/search/src/providers/search-medusa/utils/filters.ts","lineNumber":7,"sourceCode":"import { SearchTypes } from \"@medusajs/framework/types\"\nimport { MedusaError } from \"@medusajs/framework/utils\"\nimport type { Filter } from \"./api-types\"\nimport { IndexPlan, PlannedField } from \"./plan\"\n\nfunction fail(message: string): never {\n  throw new MedusaError(MedusaError.Types.NOT_ALLOWED, message)\n}\n\nfunction coerce(value: unknown, planned?: PlannedField): unknown {\n  if (value instanceof Date) {\n    return value.toISOString()\n  }\n  if (planned?.is_date && typeof value === \"string\") {\n    return new Date(value).toISOString()\n  }\n  return value\n}\n\nfunction globEscape(value: string): string {\n  return value.replace(/([*?[\\]\\\\])/g, \"\\\\$1\")\n}\n\nfunction sqlLikeToGlob(value: string): string {\n  return value","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/search/src/providers/search-medusa/utils/filters.ts#L1-L25","documentation":"This is the filter-normalization error channel (filters.ts fail()): toSearchFilter/normalizeFacetRequests throw NOT_ALLOWED when a search filter cannot be translated to the provider's filter API — typically unsupported filter operators or values that don't match the planned field type (see the coerce/plan mapping).","triggerScenarios":"Passing input.filters with an operator or value shape the provider cannot map onto a planned field: filtering a date field with a non-parsable value, filtering on an unknown/unplanned field, or a combination the provider's Filter model rejects.","commonSituations":"Storefront filter code sending raw query-string values (strings for numeric fields), filters referencing fields removed from the index after a settings change, or reusing filter payloads built for a different search provider (Meilisearch/Algolia syntax).","solutions":["Coerce query params to the field's declared type before passing filters (numbers for numeric fields, ISO strings for dates)","Filter only on fields present and filterable in the index definition","Catch and log the MedusaError message — it names the exact field/value that failed normalization"],"exampleFix":"// before\nfilters: { price: { gte: req.query.minPrice } } // string from query string\n// after\nfilters: {\n  price: {\n    gte: req.query.minPrice ? Number(req.query.minPrice) : undefined,\n  },\n}\n","handlingStrategy":"validation","validationCode":"function coerceFilters(filters, spec) {\n  const out = {}\n  for (const [field, cond] of Object.entries(filters)) {\n    if (!spec[field]) continue\n    out[field] = Object.fromEntries(\n      Object.entries(cond).map(([op, v]) => [\n        op,\n        spec[field] === \"date\" ? new Date(v).toISOString() : spec[field] === \"number\" ? Number(v) : v,\n      ])\n    )\n  }\n  return out\n}","typeGuard":"const isCoercibleFilterValue = (v, type) =>\n  type === \"number\" ? !Number.isNaN(Number(v)) : type === \"date\" ? !Number.isNaN(Date.parse(v)) : typeof v === \"string\"","tryCatchPattern":"try { await search(...) } catch (e) { if (e instanceof MedusaError && e.type === \"not_allowed\") { /* log e.message naming the bad filter, strip it, retry */ } throw e }","preventionTips":["Parse and coerce query params to typed filter values before calling search","Only filter on fields declared in the index","Centralize filter construction in one helper with a field-type spec map"],"tags":["search","filters","type-coercion","query-validation"],"backgroundTag":"invalid-filter-syntax","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}