{"record":{"id":"85c05bc12e4d05ad","repo":"mastra-ai/mastra","slug":"your-schema-is-async-which-is-not-supported-plea","errorCode":null,"errorMessage":"Your schema is async, which is not supported. Please use a sync schema.","messagePattern":"Your schema is async, which is not supported\\. Please use a sync schema\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/tools/tool.ts","lineNumber":39,"sourceCode":" * Marker to identify Mastra tools even when `instanceof` fails.\n * This can happen in environments like Vite SSR where the same module\n * may be loaded multiple times, creating different class instances.\n * Uses Symbol.for() so the same symbol is shared across module copies.\n * Follows the naming convention: <org>.<product>.<category>.<className>\n */\nexport const MASTRA_TOOL_MARKER = Symbol.for('mastra.core.tool.Tool');\n\ntype RequestContextEncoder = (values: Record<string, unknown>) => Record<string, unknown> | undefined;\ntype RequestContextInputValidator = (values: Record<string, unknown>) => boolean;\n\nfunction getRequestContextInputValidator(schema: PublicSchema): RequestContextInputValidator {\n  const standardSchema = toStandardSchema(schema);\n\n  return values => {\n    try {\n      const result = standardSchema['~standard'].validate(values);\n      if (result instanceof Promise) {\n        throw new Error('Your schema is async, which is not supported. Please use a sync schema.');\n      }\n      return !('issues' in result) || !result.issues?.length;\n    } catch {\n      return false;\n    }\n  };\n}\n\nfunction getRequestContextEncoder(schema: PublicSchema | undefined): RequestContextEncoder | undefined {\n  if (!schema || (typeof schema !== 'object' && typeof schema !== 'function')) {\n    return undefined;\n  }\n\n  const encodableSchema = schema as {\n    safeEncode?: (value: unknown) => { success: boolean; data?: unknown };\n    '~standard'?: { vendor?: string };\n  };\n  if (encodableSchema['~standard']?.vendor !== 'zod' || typeof encodableSchema.safeEncode !== 'function') {","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/tools/tool.ts#L21-L57","documentation":"Mastra validates request-context values against the tool's schema using the Standard Schema `validate` interface, which only supports synchronous validation. If the returned result is a Promise (i.e. the schema performs async checks like `z.refine` with async functions, async superRefine, or DB-backed checks), validation is silently impossible for this code path, so the library throws immediately. Note: the throw is inside a try/catch that returns `false` (rejecting the value), so the error is caught internally — but it originates here when an async schema is supplied.","triggerScenarios":"Passing a schema containing async refinements/transforms (e.g. `z.string().refine(async v => await checkDb(v))`, async `.superRefine`, or async `.transform`) as the request-context or tool input schema, so `standardSchema['~standard'].validate(values)` resolves to a Promise.","commonSituations":"Adding a uniqueness check against a database inside a refine; copying a schema from an API route that already uses async validators; migrating from plain z.object to schemas with async transforms for secret redaction.","solutions":["Remove all async refinements/transforms from the request-context/tool input schema and keep it fully synchronous.","Move the async check (e.g. uniqueness/DB lookup) into the tool's `execute` function, where awaits are allowed.","If you need both sync shape validation and async business checks, split them: sync Standard Schema for the input, async logic in execute."],"exampleFix":"// before\nconst schema = z.object({ email: z.string().refine(async e => !(await db.users.exists(e))) });\n// after\nconst schema = z.object({ email: z.string().email() });\n// check uniqueness inside execute instead:\nasync execute({ context }) {\n  if (await db.users.exists(context.email)) throw new Error('email taken');\n}","handlingStrategy":"validation","validationCode":"function isSyncSchema(schema: unknown): boolean {\n  try {\n    const std = schema as { '~standard'?: { validate: (v: unknown) => unknown } };\n    return !(std?.['~standard']?.validate({}) instanceof Promise);\n  } catch {\n    return false;\n  }\n}\n// call before passing schema to Mastra","typeGuard":"function isStandardSchema(v: unknown): v is { '~standard': { validate: (v: unknown) => { value?: unknown; issues?: readonly unknown[] } | Promise<unknown> } } {\n  return typeof v === 'object' && v !== null && '~standard' in v;\n}","tryCatchPattern":"try {\n  runWithSchema(schema);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('async, which is not supported')) {\n    throw new Error('Remove async refinements/transforms from the tool input schema.');\n  }\n  throw e;\n}","preventionTips":["Never use `refine(async ...)`, async `superRefine`, or async `.transform()` in tool/request-context input schemas.","Keep async business checks (DB/API lookups) inside tool `execute`, after sync shape validation.","Add a unit test that runs the schema's `~standard.validate` and asserts a non-Promise result."],"tags":["schema","validation","async","standard-schema"],"backgroundTag":"async-schema-not-supported","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}