{"record":{"id":"37b7ec3076f0ea29","repo":"rohitg00/ai-engineering-from-scratch","slug":"32603","errorCode":"-32603","errorMessage":"query must be a non-empty string","messagePattern":"query must be a non-empty string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.ts","lineNumber":211,"sourceCode":"      typeof clientInfo.name !== \"string\" ||\n      typeof clientInfo.version !== \"string\")\n  ) {\n    throw new RpcProblem(-32602, `${CLIENT_INFO_KEY} is malformed`);\n  }\n}\n\nfunction executeList(arguments_: JsonObject): JsonObject[] {\n  const tag = arguments_.tag;\n  const items = Object.entries(NOTES)\n    .sort(([left], [right]) => left.localeCompare(right))\n    .filter(([, note]) => !tag || note.tag === tag)\n    .map(([id, note]) => ({ id, title: note.title, tag: note.tag }));\n  return [{ type: \"text\", text: JSON.stringify(items) }];\n}\n\nfunction executeSearch(arguments_: JsonObject): JsonObject[] {\n  if (typeof arguments_.query !== \"string\" || !arguments_.query) {\n    throw new Error(\"query must be a non-empty string\");\n  }\n  const limit = arguments_.limit ?? 10;\n  if (!Number.isInteger(limit) || limit < 1 || limit > 50) {\n    throw new Error(\"limit must be an integer from 1 through 50\");\n  }\n  const query = arguments_.query.toLowerCase();\n  const hits = Object.entries(NOTES)\n    .sort(([left], [right]) => left.localeCompare(right))\n    .filter(([, note]) => note.title.toLowerCase().includes(query) || note.body.toLowerCase().includes(query))\n    .map(([id, note]) => ({ id, title: note.title }))\n    .slice(0, limit);\n  return [{ type: \"text\", text: JSON.stringify(hits) }];\n}\n\nfunction executeCreate(arguments_: JsonObject): JsonObject[] {\n  if (typeof arguments_.title !== \"string\" || typeof arguments_.body !== \"string\") {\n    throw new Error(\"title and body must be strings\");\n  }","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.ts#L193-L229","documentation":"The search tool executor requires arguments.query to be a non-empty string before it searches NOTES; a plain Error is thrown, which dispatch wraps as JSON-RPC -32603 Internal error. Tool argument validation failure surfaced as an internal error rather than invalid params.","triggerScenarios":"tools/call with name:'search' and arguments where query is missing, empty, null, or a number.","commonSituations":"LLM tool callers passing an empty query, or argument schemas that make query optional.","solutions":["Always pass a non-empty string query","Declare query as required and minLength:1 in the tool inputSchema so callers are rejected client-side"],"exampleFix":"// before\narguments: { query: '' }\n// after\narguments: { query: 'budget' }","handlingStrategy":"validation","validationCode":"if (typeof args.query !== 'string' || args.query.trim() === '') throw new ValidationError('query required');","typeGuard":"const isQuery = (v: unknown): v is string => typeof v === 'string' && v.length > 0;","tryCatchPattern":"// server returns isError:true content for tool errors; check result.isError before reading content","preventionTips":["Make query required with minLength:1 in the tool schema","Trim and reject whitespace-only queries client-side"],"tags":["mcp","tool-arguments","validation"],"backgroundTag":"invalid-tool-arguments","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}