trpc/trpc · error · Error

Could not find a validator fn

Error message

Could not find a validator fn

What it means

`getParseFn` walks the supported parser shapes (Zod-like `.parse/.parseAsync`, Yup `.validateSync`, Superstruct `.create`, Scale `.assert`, custom function, Valibot, ArkType, Standard Schema `~standard`). If none of those signatures is detected on the object passed to `.input()`/`.output()`, it throws 'Could not find a validator fn' - tRPC cannot guess how to run your validator.

Source

Thrown at packages/server/src/unstable-core-do-not-import/parser.ts:139

    // ParserScaleEsque
    return (value) => {
      parser.assert(value);
      return value as TType;
    };
  }

  if (isStandardSchema) {
    // StandardSchemaEsque
    return async (value) => {
      const result = await parser['~standard'].validate(value);
      if (result.issues) {
        throw new StandardSchemaV1Error(result.issues);
      }
      return result.value;
    };
  }

  throw new Error('Could not find a validator fn');
}

View on GitHub (pinned to acff82332d)

Solutions

  1. Confirm the validator object exposes one of the supported APIs (e.g. `parser.parse` / `parser['~standard']` / is a function).
  2. If using an unsupported library, wrap it as a plain function `(input) => result` and pass that function to `.input()`/`.output()`.
  3. Check that you're passing the schema, not the result of calling it (e.g. `z.object(...)` not `z.object(...).parse(...)`).
  4. Verify the validator library version matches what tRPC's detection logic expects.

Example fix

// before - JSON schema object is not a parser
.input({ type: 'object', properties: { id: { type: 'number' } } })

// after - wrap an unsupported validator in a function
.input((raw) => {
  const v = assertUserShape(raw);
  return v;
})
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify the validator exposes a supported API before passing it
function isParser(v: unknown): boolean {
  return typeof v === 'function'
    || (v && typeof (v as any).parse === 'function')
    || (v && typeof (v as any).parseAsync === 'function')
    || (v && typeof (v as any).validateSync === 'function')
    || (v && typeof (v as any).create === 'function')
    || (v && typeof (v as any).assert === 'function')
    || (v && '~standard' in (v as any));
}

Type guard

import type { Parser } from '@trpc/server';
function asParser<T>(p: Parser): Parser { return p; }

Try / catch

try { t.procedure.input(maybeParser); }
catch (e) {
  if (/Could not find a validator fn/.test(e.message))
    throw new Error('Wrap your validator as a function: (v) => lib.validate(v)');
  throw e;
}

Prevention

When it happens

Trigger: Passing a schema class instance whose API doesn't match any supported shape (e.g. a raw JSON Schema object, a Joi schema, or a hand-written class); passing a string/number/null by mistake; a tree-shaking/bundling bug that strips the `parse`/`validate` methods off the prototype; passing an already-unwrapped function that lacks the expected signature.

Common situations: Using a validator library tRPC doesn't auto-detect; double-wrapping (`z.object(...).parse` instead of `z.object(...)`); version skew where the validator lib changed its API; passing `schema.refine(...)` result that returns a different shape.

Related errors


AI-assisted analysis of trpc/trpc@acff82332d (2026-08-12). Data as JSON: /api/errors/659bcbb8bd8aaef8. Report an issue: GitHub.