Skip to content

getParseErrorMessage prioritizes error.message which bypasses custom Zod errors #1415

@mozmo15

Description

@mozmo15

Context:

Since we use Zod to define the schema of our tool arguments, a MCP error -32602: Input validation error: Invalid arguments for tool occurs outside the scope of our tool callback and custom error handlers. Our only option then is to use the Zod api to customize our schema definitions with our own errors when needed.

Issue:

With zod4, there has been significant overhauling in how custom errors are handled and defined. However the method getParseErrorMessage, which is used to extract the correct error message to show when schema validation fails, has not been updated in some time
https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/core/src/util/zodCompat.ts#L195

The issue is that custom error messages live under the zodErrorObject.issues array, with each issue in the array having a message property. This is where custom error messages, defined for example with

 z.union(literals, {
      error: () => ({
        message: customErrorMessage,
      }),
    });

would live.

Example:

In our case, we have a very large union for which we are defining a concise custom error message. Under zodErrorObject.message, the default error lives which contains all the possible literals under that union. It looks like this:

MCP error -32602: Input validation error: Invalid arguments for tool update_country: [
  {
    "code": "invalid_union",
    "errors": [
      [
        {
          "code": "invalid_value",
          "values": [
            "Egypt"
          ],
          "path": [],
          "message": "Invalid input: expected \"Egypt\""
        }
      ],
      [
        {
          "code": "invalid_value",
          "values": [
            "Ethiopia"
          ],
          "path": [],
          "message": "Invalid input: expected \"Ethiopia\""
        }
      ],
... all the other countries
} ]

This can easily overwhelm the context window when returned to the client in our case. But defining a custom error does not help since it is still not used by getParseErrorMessage, and the long default message takes priority

Suggestion/discussion:

Since error.message is a more generic fallback, Im hesitant to suggest moving it second, but I also believe it may be an assumption based on old Zod behaviour in aggregating errors under error.message, which does not apply in zod4 anymore. It may make sense now with Zod's addition of custom errors exclusively under the issues properties to check that first under getParseErrorMessage

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions