{"record":{"id":"217fd1a7d1a7eacc","repo":"rohitg00/agentmemory","slug":"query-must-be-a-non-empty-string","errorCode":null,"errorMessage":"query must be a non-empty string","messagePattern":"query must be a non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/functions/query-expansion.ts","lineNumber":76,"sourceCode":"    entityExtractions.push(match[1].trim());\n  }\n\n  return {\n    original: \"\",\n    reformulations,\n    temporalConcretizations,\n    entityExtractions,\n  };\n}\n\nexport function registerQueryExpansionFunction(\n  sdk: ISdk,\n  provider: MemoryProvider,\n): void {\n  sdk.registerFunction(\"mem::expand-query\", \n    async (data: { query: string; maxReformulations?: number } | undefined) => {\n      if (!data || typeof data.query !== \"string\" || !data.query.trim()) {\n        logger.warn(\"Invalid expand-query payload\");\n        return { success: false, error: \"query must be a non-empty string\" };\n      }\n      const rawMaxR = Number(data.maxReformulations);\n      const maxR = Number.isFinite(rawMaxR)\n        ? Math.max(1, Math.min(10, Math.floor(rawMaxR)))\n        : 5;\n      const query = data.query.trim();\n\n      try {\n        const response = await provider.compress(\n          QUERY_EXPANSION_SYSTEM,\n          `Expand this query for memory retrieval:\\n\\n\"${query}\"`,\n        );\n\n        const parsed = parseExpansionXml(response);\n        if (!parsed) {\n          logger.warn(\"Failed to parse query expansion\");\n          return {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/rohitg00/agentmemory/blob/e04ba88819c365c9acf9d6661ea802143e728bd6/src/functions/query-expansion.ts#L58-L94","documentation":"The mem::expand-query iii function validates its payload before doing work: if data is missing, query is not a string, or query is empty/whitespace, it logs a warning and returns { success: false, error: 'query must be a non-empty string' } rather than throwing. Callers (e.g. MCP tool handlers) receive this structured failure result and must surface it.","triggerScenarios":"Calling sdk.trigger({ function_id: 'mem::expand-query' }) with payload undefined, { query: 123 }, { query: \"\" }, or { query: \"   \" }, or a handler that forwards an unvalidated/missing query argument.","commonSituations":"An MCP client sends a memory_expand_query tool call without the query argument; a handler passes raw request body through without whitelisting/typing; a refactor renamed the field so query is undefined at runtime.","solutions":["Ensure the payload is { query: \"<non-empty string>\" } and that the argument is actually bound (log it before triggering).","Fix the calling handler to validate args.query with typeof checks before sdk.trigger, per the MCP-handler pattern.","Check the tool/argument name on the client side hasn't changed (e.g. query vs q).","Handle the { success: false } result in the caller instead of assuming success."],"exampleFix":"// before\nawait sdk.trigger({ function_id: \"mem::expand-query\", payload: { q: text } });\n// after\nif (typeof text !== \"string\" || !text.trim()) throw new Error(\"query required\");\nawait sdk.trigger({ function_id: \"mem::expand-query\", payload: { query: text } });","handlingStrategy":"validation","validationCode":"function buildExpandQueryPayload(args: Record<string, unknown>) {\n  const query = typeof args[\"query\"] === \"string\" ? args[\"query\"] : \"\";\n  if (!query.trim()) throw new Error(\"query must be a non-empty string\");\n  return { query, maxReformulations: 5 };\n}","typeGuard":"const isExpandQueryPayload = (d: unknown): d is { query: string; maxReformulations?: number } =>\n  typeof d === \"object\" && d !== null &&\n  typeof (d as any).query === \"string\" && (d as any).query.trim().length > 0;","tryCatchPattern":"const res = await sdk.trigger({ function_id: \"mem::expand-query\", payload });\nif (!res.success) {\n  console.error(`expand-query rejected: ${res.error}`);\n}","preventionTips":["Validate and whitelist tool args at the MCP handler boundary before sdk.trigger.","Always pass the exact field name query (not q/search).","Check res.success before using expansion results.","Trim/guard whitespace-only strings client-side."],"tags":["validation","payload","iii-function","query-expansion"],"backgroundTag":"schema-validation-failed","analyzedSha":"e04ba88819c365c9acf9d6661ea802143e728bd6","analyzedAt":"2026-08-30T01:07:40.754Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}