rohitg00/ai-engineering-from-scratch · error · Error

-32603

-32603

Error message

query must be a non-empty string

What it means

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.

Source

Thrown at phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.ts:211

      typeof clientInfo.name !== "string" ||
      typeof clientInfo.version !== "string")
  ) {
    throw new RpcProblem(-32602, `${CLIENT_INFO_KEY} is malformed`);
  }
}

function executeList(arguments_: JsonObject): JsonObject[] {
  const tag = arguments_.tag;
  const items = Object.entries(NOTES)
    .sort(([left], [right]) => left.localeCompare(right))
    .filter(([, note]) => !tag || note.tag === tag)
    .map(([id, note]) => ({ id, title: note.title, tag: note.tag }));
  return [{ type: "text", text: JSON.stringify(items) }];
}

function executeSearch(arguments_: JsonObject): JsonObject[] {
  if (typeof arguments_.query !== "string" || !arguments_.query) {
    throw new Error("query must be a non-empty string");
  }
  const limit = arguments_.limit ?? 10;
  if (!Number.isInteger(limit) || limit < 1 || limit > 50) {
    throw new Error("limit must be an integer from 1 through 50");
  }
  const query = arguments_.query.toLowerCase();
  const hits = Object.entries(NOTES)
    .sort(([left], [right]) => left.localeCompare(right))
    .filter(([, note]) => note.title.toLowerCase().includes(query) || note.body.toLowerCase().includes(query))
    .map(([id, note]) => ({ id, title: note.title }))
    .slice(0, limit);
  return [{ type: "text", text: JSON.stringify(hits) }];
}

function executeCreate(arguments_: JsonObject): JsonObject[] {
  if (typeof arguments_.title !== "string" || typeof arguments_.body !== "string") {
    throw new Error("title and body must be strings");
  }

View on GitHub (pinned to 39ea8a1c6d)

Solutions

  1. Always pass a non-empty string query
  2. Declare query as required and minLength:1 in the tool inputSchema so callers are rejected client-side

Example fix

// before
arguments: { query: '' }
// after
arguments: { query: 'budget' }
Defensive patterns

Strategy: validation

Validate before calling

if (typeof args.query !== 'string' || args.query.trim() === '') throw new ValidationError('query required');

Type guard

const isQuery = (v: unknown): v is string => typeof v === 'string' && v.length > 0;

Try / catch

// server returns isError:true content for tool errors; check result.isError before reading content

Prevention

When it happens

Trigger: tools/call with name:'search' and arguments where query is missing, empty, null, or a number.

Common situations: LLM tool callers passing an empty query, or argument schemas that make query optional.

Related errors


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/37b7ec3076f0ea29. Report an issue: GitHub.