medusajs/medusa · error · MedusaError
Deleting Medusa search documents requires filters
Error message
Deleting Medusa search documents requires filters
What it means
Thrown by the Medusa search provider's deleteDocuments when the delete request has no usable filters — toSearchFilter(filters) compiles to null. Since deletion without a filter would wipe the index, the provider requires at least one concrete filter and rejects with INVALID_ARGUMENT.
Source
Thrown at packages/modules/search/src/providers/search-medusa/services/medusa-search.ts:174
for (const chunk of this.chunkRows(rows)) {
await remote.write({
upsert_rows: chunk,
schema: plan.schema,
distance_metric: plan.options.distance_metric,
})
}
return this.task(index)
}
async deleteDocuments({
index,
filters,
}: SearchTypes.SearchDeleteDocumentsInput): Promise<SearchTypes.SearchTask> {
const compiled = toSearchFilter(filters)
if (!compiled) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"Deleting Medusa search documents requires filters"
)
}
let remaining = true
while (remaining) {
const response = await this.index(index).write({
delete_by_filter: compiled,
delete_by_filter_allow_partial: true,
})
remaining = response.rows_remaining === true
}
return this.task(index)
}
async clearIndex({View on GitHub (pinned to 5e06e544a2)
Solutions
- Pass at least one concrete filter that identifies the documents to delete, e.g. { id: [...] } or a field equality filter
- Guard dynamic filter builders: skip the delete call entirely when the compiled filter is empty
- For full-index cleanup, use a deliberate index-wide delete/reset API instead of an empty filter
Example fix
// before
await searchProvider.deleteDocuments({ index, filters: undefined })
// after
await searchProvider.deleteDocuments({ index, filters: { product_id: productId } }) Defensive patterns
Strategy: type-guard
Validate before calling
function hasDeleteFilters(filters: unknown): boolean {
return (
!!filters &&
typeof filters === "object" &&
Object.keys(filters as object).length > 0
)
}
if (!hasDeleteFilters(filters)) {
logger.warn('Skipping deleteDocuments: no filters provided')
} else {
await provider.deleteDocuments({ index, filters })
} Type guard
interface ConcreteFilter { [k: string]: unknown }
function isConcreteFilter(f: unknown): f is ConcreteFilter {
return !!f && typeof f === 'object' && Object.values(f).some((v) => v !== undefined && v !== null)
} Try / catch
try {
await provider.deleteDocuments({ index, filters })
} catch (e) {
if (e instanceof MedusaError && e.type === MedusaError.Types.INVALID_ARGUMENT) {
// filters compiled to nothing: never retry blindly; fix the filter first
logger.error(`deleteDocuments rejected: ${e.message}`)
} else throw e
} Prevention
- Never call deleteDocuments without an explicit identifying filter
- Short-circuit dynamic filter builders when the result is empty
- Use index reset APIs for intentional full deletions
When it happens
Trigger: Calling deleteDocuments({ index }) with filters omitted, empty, or containing only clauses that compile to nothing (e.g. empty $and, undefined values).
Common situations: Fire-and-forget cleanup jobs that pass only an index, optional chaining producing undefined filters ({ filters: maybeFilters?.x }), or building filters dynamically so they end up empty at runtime.
Related errors
- --paths must be a directory - ${additionalPath}
- --base must be a file - ${baseFile}
- insufficient_inventory
- not_found
- invalid_data
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/46c846045c5db5fc.
Report an issue: GitHub.