question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Detailed errors for union schema

See original GitHub issue

I’d like to have more information or even TS-like details for union errors (Add a flag to parse?):

My types:

export const T = z.union([
  A, B, C
])

So what I currently get from this is:

     Error: 1 validation issue(s)

  Issue #0: invalid_union at 
  Invalid input

What I’d like to get is something more similar to this:

'{ t: 1 }' is not assignable to parameter of type '{ a: number } | { b: number }'

Is this sensible|doable|easy|hard?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
chrbalacommented, Aug 15, 2020

It seems like this could be improved with a union resolver function as I proposed in https://github.com/vriad/zod/issues/100#issuecomment-667584554. As long as the input matches well enough (e.g. with a discriminator field) to be recognized as an element of the union in userland, the errors could be simplified to a single case instead of needing to resolve unionErrors within an invalid_union error type.

enum Number {
  FLOAT,
  INT,
}
const floatObj = z.object({
  kind: z.literal(Number.FLOAT),
  value: z.number(),
});
const intObj = z.object({
  kind: z.literal(Number.INT),
  value: z.number().refine(n => n % 1 === 0, "not_int"),
});

const resolver = (val: unknown) =>
  val && typeof val === 'object'
    ? val.kind === Number.FLOAT
      ? floatObj
      : intObj
    : null;

const union = z.union([floatObj, intObj], resolver);

const input = {
  kind: Number.INT,
  value: 5.5,
};

union.parse(input); // same result as intObj.parse(input);

So here, parsing input with into either the union or intObj schemas should result in:

Error: 1 validation issue(s)

  Issue #0: custom_error at value
  not_int
3reactions
colinhackscommented, Aug 15, 2020

Simple answer:

You have access to all this error information already. Zod throws a special subclass of Error called ZodError that contains detailed error information in the .errors property:

import * as z from "zod";

try {
  z.union([z.object({ a: z.number() }), z.object({ b: z.number() })]).parse({ t: 1 });
} catch (err) {
  if (err instanceof z.ZodError) {
    console.log(JSON.stringify(err.errors, null, 2));
  }
}

This wil print:

[
  {
    "code": "invalid_union",
    "unionErrors": [
      {
        "errors": [
          {
            "code": "unrecognized_keys",
            "keys": [
              "t"
            ],
            "path": [],
            "message": "Unrecognized key(s) in object: 't'"
          },
          {
            "code": "invalid_type",
            "expected": "number",
            "received": "undefined",
            "path": [
              "a"
            ],
            "message": "Required"
          }
        ]
      },
      {
        "errors": [
          {
            "code": "unrecognized_keys",
            "keys": [
              "t"
            ],
            "path": [],
            "message": "Unrecognized key(s) in object: 't'"
          },
          {
            "code": "invalid_type",
            "expected": "number",
            "received": "undefined",
            "path": [
              "b"
            ],
            "message": "Required"
          }
        ]
      }
    ],
    "path": [],
    "message": "Invalid input"
  }
]


More complicated answer:

Error handling in Zod is complex enough that I split it into it’s own README: https://github.com/vriad/zod/blob/master/ERROR_HANDLING.md

That guide explains how to do this. Getting the exact error information you need can feel a bit like spelunking, but I’m pretty sure this complexity is irreducible. Believe me, I’ve tried 😃

Here’s how to do what you want:

import * as z from ‘.’;

try {
  z.union([z.string(), z.number()]).parse(true);
} catch (err) {
  // check for ZodError
  if (err instanceof z.ZodError) {
    // loop over suberrors
    for (const suberr of err.errors) {
      // check if suberror is an `invalid_union` error
      // inside the for loop, `suberr` will have additional 
      // properties specific to union errors (e.g. `unionErrors`)
      if (suberr.code === z.ZodErrorCode.invalid_union) {
        // suberr.unionErrors is an array of ZodErrors for each union component
        suberr.unionErrors; // ZodError[]
        console.log(suberr.unionErrors.map(e => e.message).join("\n"))
      }
    }
  }
}

If you don’t want to write this error handling code every time you want to handle union errors you should write your own Error Map which lets you customize the message for every kind of error in Zod. You then pass your error map as a parameter to .parse(). Instructions for doing so are all explained in the guide. 👍

You can get a sense for all the error data available to you like this:

Read more comments on GitHub >

github_iconTop Results From Across the Web

Detailed errors for union schema #117 - colinhacks/zod - GitHub
I'd like to have more information or even TS-like details for union errors (Add a flag to parse?): My types: export const T...
Read more >
Handling GraphQL errors like a champ with unions and ...
Error handling can be frustrating in GraphQL. This post shows you how to use unions and interfaces to handle errors in a more...
Read more >
GraphQL Error Handling with Union Types - Episode #30
The GraphQL Spec briefly discusses error handling in its simplest form for both requests, and field, but if you're used to working with ......
Read more >
Unions and interfaces - Apollo GraphQL Docs
Unions and interfaces are abstract GraphQL types that enable a schema field to return one of multiple object types. Union type.
Read more >
GraphQL mutation errors (union errors with interface aka 6a ...
This article tries to show the concrete implementation of stage 6a mutation error handling from Marc-Andre Giroux Guide to graphql errors under ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found