rohitg00/ai-engineering-from-scratch · error · RpcProblem
-32602
-32602
Error message
params must be an object
What it means
Every request to this server must carry params as a plain object (not an array, string, number, or null); arrays and non-objects are rejected with -32602 Invalid params. This is stricter than bare JSON-RPC because handlers read named fields off params.
Source
Thrown at phases/13-tools-and-protocols/07-building-an-mcp-server/code/main.ts:168
return {
resultType: "complete",
...payload,
...(cache ?? {}),
_meta: { [SERVER_INFO_KEY]: { ...SERVER_INFO } },
};
}
function validateRequest(message: JsonRpcRequest): void {
if (message.jsonrpc !== "2.0" || typeof message.method !== "string") {
throw new RpcProblem(-32600, "Invalid Request");
}
const requestId: unknown = message.id;
if (requestId !== undefined && !isValidRequestId(requestId)) {
throw new RpcProblem(-32600, "id must be a string or integer");
}
const params = message.params;
if (!params || typeof params !== "object" || Array.isArray(params)) {
throw new RpcProblem(-32602, "params must be an object");
}
const meta = params._meta;
if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
throw new RpcProblem(-32602, "params._meta is required");
}
const requested = meta[VERSION_KEY];
if (typeof requested !== "string") {
throw new RpcProblem(-32602, `${VERSION_KEY} is required`);
}
if (!SUPPORTED_VERSIONS.includes(requested)) {
throw new RpcProblem(-32022, "Unsupported protocol version", {
requested,
supported: [...SUPPORTED_VERSIONS],
});
}
const capabilities = meta[CAPABILITIES_KEY];
if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) {
throw new RpcProblem(-32602, `${CAPABILITIES_KEY} is required`);View on GitHub (pinned to 39ea8a1c6d)
Solutions
- Wrap arguments in an object: params:{...} not params:[...]
- Double-check the JSON serializer is not flattening objects to strings
Example fix
// before
{ jsonrpc: '2.0', id: 1, method: 'tools/list', params: [] }
// after
{ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {} } Defensive patterns
Strategy: validation
Validate before calling
if (!params || typeof params !== 'object' || Array.isArray(params)) params = {}; Type guard
function isJsonObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Prevention
- Always send named-argument objects, never positional arrays
- Type params as Record<string, unknown> in the client so the compiler blocks arrays
When it happens
Trigger: Calling dispatch with params:[], params:'x', params:5, or params omitted and no default applied (dispatch does pass {} when params is undefined, but validateRequest rejects anything present but non-object).
Common situations: Clients modeling tool arguments as arrays, or serializing params to a JSON string before sending.
Related errors
- -32600
- -32601
- Budget.step requires non-negative tokens and dollars
- path escapes sandbox: ${(err as Error).message}
- _meta must be an object
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/e44e6ccf99f4ae4a.
Report an issue: GitHub.