TanStack / TanStack/router

inputValidator validation errors lose structure during serialization - issues array becomes JSON string in error.message

Open
#6,428 3 comments 10 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
15.1k
Forks
1.9k
Avg merge
1d 20h
Merged PRs (30d)
143

Description

Describe the issue

Not a big but rather a feedback / issue I'd like to raise

When inputValidator fails (e.g., with Zod), the validation error is serialized in a way that loses all structure. The Zod issues array is JSON-stringified and stuffed into error.message, making it unusable on the client without manual parsing.

Your minimal, reproducible example
// Server function
export const myFn = createServerFn({ method: 'POST' })
  .inputValidator(z.object({
    name: z.string().min(2, 'Name must be at least 2 characters'),
    age: z.number().min(18, 'Must be 18+'),
  }))
  .handler(async ({ data }) => data)

// Client - send invalid data
mutation.mutate({ data: { name: 'A', age: 15 } })
Current behavior

Client receives:

{
  name: "Error",  // Lost ZodError type
  message: '[{"origin":"string","code":"too_small","path":["name"],"message":"Name must be at least 2 characters"},...]',
  // ↑ JSON string, not structured data
  // No .issues, no .code - just raw JSON crammed into message
}
Expected behavior

Client should receive structured error data:

{
  code: 'VALIDATION_ERROR',
  message: 'Validation failed',
  issues: [
    { path: ['name'], message: 'Name must be at least 2 characters' },
    { path: ['age'], message: 'Must be 18+' },
  ]
}

Or at minimum, preserve the error structure so error.issues is accessible.

Why this matters
  1. Can't distinguish error types - Is this validation error or server crash? Both are just Error
  2. Can't show field-level errors - Must JSON.parse(error.message) to extract issues
  3. Breaks standard patterns - HTTP APIs return 422 with structured validation errors; this pattern makes that impossible
How other frameworks handle this
  • tRPC: Preserves Zod error structure via superjson
  • Remix: Actions return structured data, not thrown errors
  • Next.js Server Actions: Can return error objects with structure
Suggested solutions

Either:

  1. Serialize known error types (ZodError) with their structure preserved
  2. Return validation failures as data (like HTTP 422 response body) instead of thrown errors
  3. Allow custom error serializers via configuration
Additional context

Related discussion: #2935 - covers broader error typing issues from server functions

Platform
  • @tanstack/react-start: 1.153.1
  • Zod: 4.x
  • Runtime: Cloudflare Workers

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the inputValidator error serialization path for server functions, using the Zod 4 example in this issue as the reproduction case. Read related discussion #2935 for existing error-typing context; done means validation failures retain accessible structured issue data on the client without requiring JSON.parse(error.message).

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.