mastra-ai/mastra · error

Your schema is async, which is not supported. Please use a s

Error message

Your schema is async, which is not supported. Please use a sync schema.

What it means

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.

Source

Thrown at packages/core/src/tools/tool.ts:39

 * Marker to identify Mastra tools even when `instanceof` fails.
 * This can happen in environments like Vite SSR where the same module
 * may be loaded multiple times, creating different class instances.
 * Uses Symbol.for() so the same symbol is shared across module copies.
 * Follows the naming convention: <org>.<product>.<category>.<className>
 */
export const MASTRA_TOOL_MARKER = Symbol.for('mastra.core.tool.Tool');

type RequestContextEncoder = (values: Record<string, unknown>) => Record<string, unknown> | undefined;
type RequestContextInputValidator = (values: Record<string, unknown>) => boolean;

function getRequestContextInputValidator(schema: PublicSchema): RequestContextInputValidator {
  const standardSchema = toStandardSchema(schema);

  return values => {
    try {
      const result = standardSchema['~standard'].validate(values);
      if (result instanceof Promise) {
        throw new Error('Your schema is async, which is not supported. Please use a sync schema.');
      }
      return !('issues' in result) || !result.issues?.length;
    } catch {
      return false;
    }
  };
}

function getRequestContextEncoder(schema: PublicSchema | undefined): RequestContextEncoder | undefined {
  if (!schema || (typeof schema !== 'object' && typeof schema !== 'function')) {
    return undefined;
  }

  const encodableSchema = schema as {
    safeEncode?: (value: unknown) => { success: boolean; data?: unknown };
    '~standard'?: { vendor?: string };
  };
  if (encodableSchema['~standard']?.vendor !== 'zod' || typeof encodableSchema.safeEncode !== 'function') {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Remove all async refinements/transforms from the request-context/tool input schema and keep it fully synchronous.
  2. Move the async check (e.g. uniqueness/DB lookup) into the tool's `execute` function, where awaits are allowed.
  3. If you need both sync shape validation and async business checks, split them: sync Standard Schema for the input, async logic in execute.

Example fix

// before
const schema = z.object({ email: z.string().refine(async e => !(await db.users.exists(e))) });
// after
const schema = z.object({ email: z.string().email() });
// check uniqueness inside execute instead:
async execute({ context }) {
  if (await db.users.exists(context.email)) throw new Error('email taken');
}
Defensive patterns

Strategy: validation

Validate before calling

function isSyncSchema(schema: unknown): boolean {
  try {
    const std = schema as { '~standard'?: { validate: (v: unknown) => unknown } };
    return !(std?.['~standard']?.validate({}) instanceof Promise);
  } catch {
    return false;
  }
}
// call before passing schema to Mastra

Type guard

function isStandardSchema(v: unknown): v is { '~standard': { validate: (v: unknown) => { value?: unknown; issues?: readonly unknown[] } | Promise<unknown> } } {
  return typeof v === 'object' && v !== null && '~standard' in v;
}

Try / catch

try {
  runWithSchema(schema);
} catch (e) {
  if (e instanceof Error && e.message.includes('async, which is not supported')) {
    throw new Error('Remove async refinements/transforms from the tool input schema.');
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/85c05bc12e4d05ad. Report an issue: GitHub.