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
- 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.
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
- 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.
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
- ErrorCode.InvalidParams
- Invalid arguments for write: ${parsed.error}
- InvalidParams
- Invalid command: command must be a non-empty string
- Invalid command: command cannot be empty
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/fa2e1db8145855c1.
Report an issue: GitHub.