CherryHQ/cherry-studio · error · McpError

InternalError

InternalError

Error message

Python execution failed: ${errorMessage}

What it means

Catch-all at the end of the python server's CallTool handler. Every exception inside the try-block — including a zod InvalidParams McpError (see 348) and any failure from `PythonService.executeScript` (Pyodide) — is logged and re-thrown as McpError InternalError. Because the catch does not re-throw McpError (unlike memory.ts), genuine validation failures and genuine execution failures are indistinguishable to the client.

Source

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

        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
            }
          ]
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error)
        logger.error(`Python execution error: ${errorMessage}`)

        throw new McpError(ErrorCode.InternalError, `Python execution failed: ${errorMessage}`)
      }
    })
  }
}

export default PythonServer

View on GitHub (pinned to 726446b54c)

Solutions

  1. Read the wrapped message — it preserves the underlying Python traceback/cause verbatim.
  2. For dependency errors, declare packages via PEP 723 `# /// script` metadata at the top of `code`.
  3. For timeouts, pass a larger `timeout` (clamped to MAX_TIMEOUT_MS = 10 min) or reduce work.
  4. Server-side: add `if (error instanceof McpError) throw error` before wrapping so validation errors keep their real code.
Defensive patterns

Strategy: try-catch

Validate before calling

// Reduce runtime failures: declare third-party deps via PEP 723 and keep code under the timeout.
function wrapPythonCode(userCode: string, deps: string[] = []): string {
  const meta = deps.length
    ? `# /// script\n# dependencies = ${JSON.stringify(deps)}\n# ///\n`
    : ''
  return `${meta}${userCode}`
}

Type guard

const isMcpInternalError = (e: unknown): boolean =>
  e instanceof Error && (e as any).code === ErrorCode.InternalError

Try / catch

try {
  await client.callTool({ name: 'python_execute', arguments: { code, timeout: 30000 } })
} catch (e) {
  if (e instanceof McpError && e.code === ErrorCode.InternalError) {
    // e.message preserves the Python traceback / zod message verbatim — classify by content
    if (/Timeout|timed out/i.test(e.message)) {/* shorten code or raise timeout (<= 600000) */}
    else if (/ModuleNotFoundError|ImportError/.test(e.message)) {/* add PEP 723 dep */}
    else {/* genuine Python exception — fix the submitted code */}
  }
  throw e
}

Prevention

When it happens

Trigger: Pyodide raises a Python exception (`SyntaxError`, `NameError`, import failure, timeout); the sandboxed runtime crashes; a zod validation failure (re-wrapped here); a dependency listed via PEP 723 fails to install.

Common situations: Buggy submitted code; missing third-party package not declared via PEP 723 metadata; execution exceeds the clamped timeout; non-string `code` slipping past validation.

Related errors


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