krisk/Fuse · error · Error

Unknown query operator '${op}'

Error message

Unknown query operator '${op}'

What it means

The operator wrapped by $not must be a known operator. When isKnownOperator(innerOp) is false, compileClause throws UNKNOWN_QUERY_OPERATOR with the inner operator's name. This is the negation-path check before an inverse matcher is looked up.

Source

Thrown at src/search/extended/objectQuery.ts:110

        )
      }

      const innerOp = innerOps[0]
      if (innerOp === NOT) {
        throw new Error(
          ErrorMsg.INVALID_FIELD_QUERY(keyPath, '$not cannot be nested')
        )
      }
      if (innerOp === AND || innerOp === OR) {
        throw new Error(
          ErrorMsg.INVALID_FIELD_QUERY(
            keyPath,
            `${innerOp} cannot be used inside $not`
          )
        )
      }
      if (!isKnownOperator(innerOp)) {
        throw new Error(ErrorMsg.UNKNOWN_QUERY_OPERATOR(innerOp))
      }

      // `$fuzzy` and `$eq` have no inverse matcher (fuzzy is graded rather than
      // boolean; whole-string inequality is unimplemented, which is why `$ne`
      // stays reserved). Report that distinctly from "unknown operator".
      def = negatedMatcherDefForOperator(innerOp)
      if (!def) {
        throw new Error(
          ErrorMsg.INVALID_FIELD_QUERY(
            keyPath,
            `'${innerOp}' cannot be negated`
          )
        )
      }

      raw = inner[innerOp]
      valueOp = innerOp
    } else {

View on GitHub (pinned to edf2fb608e)

Solutions

  1. Fix the operator name inside $not to a supported one ($contains, $startsWith, $endsWith, $fuzzy, $eq, etc.)
  2. Check the library docs for the supported operator list — Mongo operators like $gt/$in are not available
  3. If queries come from persisted config, validate operator names against isKnownOperator before calling search

Example fix

// before
search({ title: { $not: { $gte: 'a' } } })
// after
search({ title: { $not: { $startsWith: 'a' } } })
Defensive patterns

Strategy: type-guard

Validate before calling

import { isKnownOperator } from './search/extended/matchers'
function validateNegatedOperator(clause) {
  const inner = clause && clause.$not
  if (inner) {
    const op = Object.keys(inner)[0]
    if (!isKnownOperator(op)) throw new Error(`Unknown operator '${op}' inside $not`)
  }
}

Type guard

function isKnownOperator(op) { return KNOWN_OPS.includes(op) }

Try / catch

try {
  results = search(query)
} catch (e) {
  if (e.message.includes('Unknown query operator')) {
    const op = e.message.match(/'([^']+)'/)?.[1]
    throw new Error(`'${op}' is not supported — check the operator list (typos? Mongo ops?)`)
  } else throw e
}

Prevention

When it happens

Trigger: search({ title: { $not: { $contins: 'a' } } }) — a typo'd or entirely unknown operator inside $not.

Common situations: Typos in operator names ($regex vs $regexx); using operators from another library (Mongo's $gte/$lte are not supported here); refactors that renamed operators while queries were stored as strings/config.


AI-assisted analysis of krisk/Fuse@edf2fb608e (2026-09-02). Data as JSON: /api/errors/272775cf93e0feff. Report an issue: GitHub.