{"record":{"id":"fa2e1db8145855c1","repo":"CherryHQ/cherry-studio","slug":"invalidparams-fa2e1d","errorCode":"InvalidParams","errorMessage":"Invalid arguments for python_execute: ${parsed.error.message}","messagePattern":"Invalid arguments for python_execute: (.+?)","errorType":"exception","errorClass":"McpError","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/servers/python.ts","lineNumber":92,"sourceCode":"              required: ['code']\n            }\n          }\n        ]\n      }\n    })\n\n    // Handle tool calls\n    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n      const { name, arguments: args } = request.params\n\n      if (name !== 'python_execute') {\n        throw new McpError(ErrorCode.MethodNotFound, `Tool ${name} not found`)\n      }\n\n      try {\n        const parsed = PythonExecuteArgsSchema.safeParse(args)\n        if (!parsed.success) {\n          throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for python_execute: ${parsed.error.message}`)\n        }\n\n        const { code, context } = parsed.data\n        // Clamp timeout to a sane range to prevent runaway or pointless executions.\n        const timeout = Math.min(Math.max(parsed.data.timeout, MIN_TIMEOUT_MS), MAX_TIMEOUT_MS)\n\n        logger.debug('Executing Python code via Pyodide')\n\n        const result = await application.get('PythonService').executeScript(code, context, timeout)\n\n        return {\n          content: [\n            {\n              type: 'text',\n              text: result\n            }\n          ]\n        }","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/python.ts#L74-L110","documentation":"The python MCP server validates args with `PythonExecuteArgsSchema.safeParse` (zod: `code` non-empty string, optional `context` record, optional positive `timeout`). On failure it throws McpError InvalidParams with the zod error message. NOTE: this throw lives inside the same try-block whose catch (line 111) wraps ALL errors — including this McpError — into InternalError, and the catch does not re-throw McpError. So at runtime a validation failure actually surfaces as `Python execution failed: Invalid arguments...` (InternalError), not InvalidParams. The InvalidParams code path here is effectively shadowed; treat it as a latent bug.","triggerScenarios":"Passing args where `code` is missing, empty, or not a string; `timeout` is non-positive or non-number; `context` is not a record. The zod message names the offending field.","commonSituations":"The model submits `{}` or omits `code`; `timeout` is `0` or negative; `context` is passed as an array; client schema drift.","solutions":["Always pass a non-empty `code` string: `{ \"code\": \"print(1)\" }`.","Omit `timeout`/`context` unless needed; if set, ensure `timeout` is a positive number and `context` is a flat object.","Fix the server bug: re-throw McpError before the generic catch wraps it (see exampleFix) so InvalidParams reaches the client correctly."],"exampleFix":"// before (server): InvalidParams is swallowed by the catch below\ntry {\n  const parsed = PythonExecuteArgsSchema.safeParse(args)\n  if (!parsed.success) {\n    throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for python_execute: ${parsed.error.message}`)\n  }\n  // ...\n} catch (error) {\n  throw new McpError(ErrorCode.InternalError, `Python execution failed: ...`)\n}\n\n// after (server): re-throw McpError so the real code reaches the client\n} catch (error) {\n  if (error instanceof McpError) throw error\n  throw new McpError(ErrorCode.InternalError, `Python execution failed: ${error instanceof Error ? error.message : String(error)}`)\n}","handlingStrategy":"validation","validationCode":"// Mirror PythonExecuteArgsSchema before calling\nfunction buildPythonArgs(raw: unknown) {\n  if (typeof (raw as any)?.code !== 'string' || (raw as any).code.length === 0) {\n    throw new TypeError(\"'code' must be a non-empty string\")\n  }\n  const out: { code: string; context?: Record<string, unknown>; timeout?: number } = { code: (raw as any).code }\n  if ((raw as any).context !== undefined) {\n    if (typeof (raw as any).context !== 'object' || (raw as any).context === null || Array.isArray((raw as any).context)) {\n      throw new TypeError(\"'context' must be a record\")\n    }\n    out.context = (raw as any).context\n  }\n  if ((raw as any).timeout !== undefined) {\n    if (typeof (raw as any).timeout !== 'number' || (raw as any).timeout <= 0) {\n      throw new TypeError(\"'timeout' must be a positive number\")\n    }\n    out.timeout = (raw as any).timeout\n  }\n  return out\n}","typeGuard":"const isPythonArgs = (v: unknown): v is { code: string; context?: Record<string, unknown>; timeout?: number } =>\n  typeof v === 'object' && v !== null &&\n  typeof (v as any).code === 'string' && (v as any).code.length > 0 &&\n  ((v as any).context === undefined || (typeof (v as any).context === 'object' && !Array.isArray((v as any).context))) &&\n  ((v as any).timeout === undefined || (typeof (v as any).timeout === 'number' && (v as any).timeout > 0))","tryCatchPattern":"// NOTE: due to the server bug, a validation failure surfaces as InternalError, not InvalidParams.\n// Detect it by message prefix until the server re-throws McpError correctly.\ntry {\n  await client.callTool({ name: 'python_execute', arguments: buildPythonArgs(raw) })\n} catch (e) {\n  if (e instanceof McpError && e.code === ErrorCode.InternalError && /Invalid arguments/.test(e.message)) {\n    // actually an arg-validation failure — fix args, do not treat as a runtime crash\n  }\n  throw e\n}","preventionTips":["Always pass a non-empty `code` string.","Omit optional fields unless you can satisfy their constraints.","Server-side: add `if (error instanceof McpError) throw error` in the catch so InvalidParams is not shadowed."],"tags":["mcp","validation","python-server","zod","invalid-params","latent-bug"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}