{"record":{"id":"a2dd70e63215a87d","repo":"krisk/Fuse","slug":"invalid-field-query-for-key-key-reason","errorCode":null,"errorMessage":"Invalid field query for key '${key}': ${reason}","messagePattern":"Invalid field query for key '(.+?)': (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/search/extended/objectQuery.ts","lineNumber":41,"sourceCode":"// Anything deeper, structural siblings, empty objects/arrays, unknown operators,\n// non-string or empty (post-normalization) values throw.\n//\n// Negation is `$not` wrapping exactly one operator, e.g. { $not: { $contains } }.\n// It sits alongside other operators in a clause (they AND together) and resolves\n// to the corresponding inverse matcher.\n\nconst AND = '$and'\nconst OR = '$or'\nconst NOT = '$not'\n\nfunction isPlainObject(value: unknown): boolean {\n  return isObjectLike(value) && !isArray(value)\n}\n\n// Compile an operator-only object (no `$and`/`$or`) into one AND group.\nfunction compileClause(clause: any, keyPath: string, options: any): Matcher[] {\n  if (!isPlainObject(clause)) {\n    throw new Error(\n      ErrorMsg.INVALID_FIELD_QUERY(\n        keyPath,\n        'each clause must be an operator object'\n      )\n    )\n  }\n\n  const ops = Object.keys(clause)\n  if (!ops.length) {\n    throw new Error(ErrorMsg.INVALID_FIELD_QUERY(keyPath, 'empty query'))\n  }\n\n  const group: Matcher[] = []\n  for (let i = 0; i < ops.length; i += 1) {\n    const op = ops[i]\n\n    if (op === AND || op === OR) {\n      throw new Error(","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/krisk/Fuse/blob/edf2fb608eca0461508d1d71317e6e58309ffada/src/search/extended/objectQuery.ts#L23-L59","documentation":"compileClause compiles an extended-search field clause into matchers and requires the clause to be a plain operator object (no top-level $and/$or). If the clause is not a plain object (string, array, number, null), it throws INVALID_FIELD_QUERY with the key path and reason 'each clause must be an operator object'.","triggerScenarios":"An extended-search object query like { title: 'plain string' }, { title: ['a','b'] }, or { title: null } — any non-plain-object clause routed through the object-query compiler.","commonSituations":"Mixing plain-string query values with object syntax in extended search; queries built from JSON where values arrive as arrays; forgetting to wrap values in a $-operator object.","solutions":["Wrap each field value in an operator object, e.g. { title: { $contains: 'world' } } instead of a bare string or array.","Validate the query shape (plain-object clauses only) before calling search.","For multiple values, use one clause per element combined with $or (or $in where supported)."],"exampleFix":"// before\nfuse.search({ title: ['world'] })\n// after\nfuse.search({ title: { $contains: 'world' } })","handlingStrategy":"validation","validationCode":"const isOperatorObject = (v) =>\n  v !== null && typeof v === 'object' && !Array.isArray(v)\nfor (const [key, value] of Object.entries(query)) {\n  if (!isOperatorObject(value)) {\n    throw new TypeError(`Field '${key}' must be an operator object like { $eq: value }`)\n  }\n}\nfuse.search(query)","typeGuard":"const isOperatorObject = (v) =>\n  v !== null && typeof v === 'object' && !Array.isArray(v)","tryCatchPattern":"try {\n  return fuse.search(query)\n} catch (e) {\n  if (e.message.startsWith('Invalid field query')) {\n    console.warn('Malformed extended-search clause:', e.message)\n    return []\n  }\n  throw e\n}","preventionTips":["Always wrap field values in $-operator objects in extended search.","Validate query shape (plain objects, $-prefixed operators) before searching.","Build queries with a helper that guarantees the operator-object schema."],"tags":["query-validation","extended-search","schema-validation"],"backgroundTag":"invalid-field-query","analyzedSha":"edf2fb608eca0461508d1d71317e6e58309ffada","analyzedAt":"2026-09-02T02:46:54.623Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}