CherryHQ/cherry-studio · error · McpError

InvalidParams

InvalidParams

Error message

Invalid arguments for python_execute: ${parsed.error.message}

What it means

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.

Source

Thrown at src/main/ai/mcp/servers/python.ts:92

              required: ['code']
            }
          }
        ]
      }
    })

    // Handle tool calls
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params

      if (name !== 'python_execute') {
        throw new McpError(ErrorCode.MethodNotFound, `Tool ${name} not found`)
      }

      try {
        const parsed = PythonExecuteArgsSchema.safeParse(args)
        if (!parsed.success) {
          throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for python_execute: ${parsed.error.message}`)
        }

        const { code, context } = parsed.data
        // Clamp timeout to a sane range to prevent runaway or pointless executions.
        const timeout = Math.min(Math.max(parsed.data.timeout, MIN_TIMEOUT_MS), MAX_TIMEOUT_MS)

        logger.debug('Executing Python code via Pyodide')

        const result = await application.get('PythonService').executeScript(code, context, timeout)

        return {
          content: [
            {
              type: 'text',
              text: result
            }
          ]
        }

View on GitHub (pinned to 726446b54c)

Solutions

  1. Always pass a non-empty `code` string: `{ "code": "print(1)" }`.
  2. Omit `timeout`/`context` unless needed; if set, ensure `timeout` is a positive number and `context` is a flat object.
  3. Fix the server bug: re-throw McpError before the generic catch wraps it (see exampleFix) so InvalidParams reaches the client correctly.

Example fix

// before (server): InvalidParams is swallowed by the catch below
try {
  const parsed = PythonExecuteArgsSchema.safeParse(args)
  if (!parsed.success) {
    throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for python_execute: ${parsed.error.message}`)
  }
  // ...
} catch (error) {
  throw new McpError(ErrorCode.InternalError, `Python execution failed: ...`)
}

// after (server): re-throw McpError so the real code reaches the client
} catch (error) {
  if (error instanceof McpError) throw error
  throw new McpError(ErrorCode.InternalError, `Python execution failed: ${error instanceof Error ? error.message : String(error)}`)
}
Defensive patterns

Strategy: validation

Validate before calling

// Mirror PythonExecuteArgsSchema before calling
function buildPythonArgs(raw: unknown) {
  if (typeof (raw as any)?.code !== 'string' || (raw as any).code.length === 0) {
    throw new TypeError("'code' must be a non-empty string")
  }
  const out: { code: string; context?: Record<string, unknown>; timeout?: number } = { code: (raw as any).code }
  if ((raw as any).context !== undefined) {
    if (typeof (raw as any).context !== 'object' || (raw as any).context === null || Array.isArray((raw as any).context)) {
      throw new TypeError("'context' must be a record")
    }
    out.context = (raw as any).context
  }
  if ((raw as any).timeout !== undefined) {
    if (typeof (raw as any).timeout !== 'number' || (raw as any).timeout <= 0) {
      throw new TypeError("'timeout' must be a positive number")
    }
    out.timeout = (raw as any).timeout
  }
  return out
}

Type guard

const isPythonArgs = (v: unknown): v is { code: string; context?: Record<string, unknown>; timeout?: number } =>
  typeof v === 'object' && v !== null &&
  typeof (v as any).code === 'string' && (v as any).code.length > 0 &&
  ((v as any).context === undefined || (typeof (v as any).context === 'object' && !Array.isArray((v as any).context))) &&
  ((v as any).timeout === undefined || (typeof (v as any).timeout === 'number' && (v as any).timeout > 0))

Try / catch

// NOTE: due to the server bug, a validation failure surfaces as InternalError, not InvalidParams.
// Detect it by message prefix until the server re-throws McpError correctly.
try {
  await client.callTool({ name: 'python_execute', arguments: buildPythonArgs(raw) })
} catch (e) {
  if (e instanceof McpError && e.code === ErrorCode.InternalError && /Invalid arguments/.test(e.message)) {
    // actually an arg-validation failure — fix args, do not treat as a runtime crash
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: The model submits `{}` or omits `code`; `timeout` is `0` or negative; `context` is passed as an array; client schema drift.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/fa2e1db8145855c1. Report an issue: GitHub.