yikart/AiToEarn · error · McpError
InternalError
InternalError
Error message
Tool result does not match outputSchema: ${validation.error.message} What it means
After a tool executes, formatToolResult validates the raw result against the tool's declared Zod outputSchema. If the tool's return value does not conform, the handler throws McpError InternalError, because returning structuredContent that violates the advertised schema would break the MCP contract.
Source
Thrown at project/aitoearn-backend/libs/nest-mcp/src/services/handlers/mcp-tools.handler.ts:46
private buildDefaultContentBlock(result: any) {
return [
{
type: 'text',
text: JSON.stringify(result),
},
]
}
private formatToolResult(result: any, outputSchema?: ZodTypeAny): any {
if (result && typeof result === 'object' && Array.isArray(result.content)) {
return result
}
if (outputSchema) {
const validation = outputSchema.safeParse(result)
if (!validation.success) {
throw new McpError(
ErrorCode.InternalError,
`Tool result does not match outputSchema: ${validation.error.message}`,
)
}
return {
structuredContent: result,
content: this.buildDefaultContentBlock(result),
}
}
return {
content: this.buildDefaultContentBlock(result),
}
}
registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest) {
if (this.registry.getTools(this.mcpModuleId).length === 0) {
this.logger.debug('No tools registered, skipping tool handlers')View on GitHub (pinned to d3aa8bea5b)
Solutions
- Align the tool's return value with its declared outputSchema (map/normalize the result before returning)
- Run the schema in a test to see the exact validation.error details
- Relax or update the outputSchema if the new shape is the intended contract
- Handle failure paths explicitly so the tool never returns null/undefined for a schema-required field
Example fix
// before
async run(args) { return (await api.getData()).data; }
// after
async run(args) {
const { data } = await api.getData();
return outputSchema.parse({ items: data.items ?? [], total: data.total ?? 0 });
} Defensive patterns
Strategy: validation
Validate before calling
const parsed = outputSchema.safeParse(result);
if (!parsed.success) {
throw new Error(`Tool result invalid: ${parsed.error.message}`);
}
return parsed.data; Type guard
function matchesOutputSchema(result: unknown, schema: z.ZodTypeAny): result is z.infer<typeof schema> {
return schema.safeParse(result).success;
} Try / catch
try {
const res = await client.callTool({ name, arguments });
return res;
} catch (e) {
if (e.code === ErrorCode.InternalError && /outputSchema/.test(e.message)) {
console.error('Server tool returned schema-violating result:', e.message);
}
throw e;
} Prevention
- Parse tool return values with outputSchema.parse inside the tool before returning
- Add unit tests asserting tool output against its declared schema
- Never return null/undefined on failure paths for schema-required fields
- Update outputSchema whenever the tool's contract changes
When it happens
Trigger: A tool implementation returns data that fails outputSchema.safeParse — missing required fields, wrong types, extra/null fields where the schema forbids them, or unparsed JSON strings returned instead of objects.
Common situations: Schema tightened after the tool code was written; tool returns null/undefined on an internal failure path; remote API change altered the response shape; developer returned the raw SDK response instead of the mapped object.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- InvalidParams
- MethodNotFound
- PlatformNotSupported
- Zod validation error (issues + input)
- ResponseCode.ConfigEditorValidationFailed
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/eaae1ba44608f63d.
Report an issue: GitHub.