#useQueryWithStatus from convex-helpers does not catch Validation Errors

8 messages · Page 1 of 1 (latest)

midnight holly
#

I'm trying to catch all the errors given in a useQuery (in particular useQueryWithStatus from convex-helpers) call but when:
const {
data: question,
isPending,
isError,
} = useQueryWithStatus(api.example.byId, {
id,
})

if he id does not match

args: {
id: zid("table"),
},

instead of having isError true, an error is raised in the frontend. is this supposed to happen?

cunning carbonBOT
#

Hi @midnight holly. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Yes, this is expected behavior with Convex and the convex-helpers' useQueryWithStatus.

When you use a validator like zid("table") in your function arguments, Convex performs both type and runtime validation. If the id you pass does not match the expected format (i.e., it's not a valid branded ID for that table), Convex will throw a validation error before your query function even runs. This error is not caught by useQueryWithStatus as a regular query error—instead, it is thrown immediately and will bubble up to your React component, potentially triggering an error boundary or causing the component to crash if not handled.

This is confirmed in community discussions and support threads, where it's explained that the validator checks are enforced before your function logic, and if the argument doesn't pass validation, an error is thrown rather than returned as part of the query status. The recommended workaround is to use v.string() as your validator and then manually check and handle invalid IDs inside your function, allowing you to throw a ConvexError or return a custom error object that can be caught by useQueryWithStatus or similar hooks. This way, you can control the error handling flow and surface errors as part of your query status rather than as uncaught exceptions in the frontend [Value does not match validator].

In summary:

  • Validation errors from argument validators (like zid) are not caught by useQueryWithStatus; they throw immediately.
  • To handle such errors gracefully, use v.string() and validate/cast/check the ID inside your function, then throw a ConvexError if needed.

This is the current expected behavior in Convex.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@midnight holly Kapa.ai is still learning and improving, please let me know how I did by reacting below.

midnight holly
cunning carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The best way to check at runtime that an id matches the expected Id<"table"> (i.e., is a valid Convex document ID for a specific table) is to use the Convex argument validator v.id("table") (or zid("table") if using Zod) in your function arguments. This ensures that only valid IDs for the specified table are accepted by your backend function. However, as you noted, this will throw a validation error if the ID is invalid, and this error is not catchable as a normal query error in the frontend—it will throw immediately [Discord: Help understanding 'Validator error: Expected ID for table...'].

If you want to check the validity of the ID at runtime on the client side, before invoking the query, you have a couple of options:

  1. Use v.string() and validate in the function:
    Pass the ID as a string and, inside your function, use ctx.db.normalizeId to check if the string is a valid ID for the table. This allows you to handle invalid IDs gracefully and return a custom error or null, rather than throwing a validation error [Discord: Value does not match validator].

    // In your Convex function
    export const getById = query({
      args: { id: v.string() },
      handler: async (ctx, args) => {
        const id = ctx.db.normalizeId("table", args.id);
        if (!id) {
          throw new ConvexError("Invalid ID");
        }
        const doc = await ctx.db.get(id);
        // ... handle doc
      },
    });
    
#
  1. Client-side check (limited):
    There is no official client-side utility in Convex to check if a string is a valid Id<"table">, since the ID format is an implementation detail. However, you could implement a basic check for the string format (e.g., length, allowed characters), but this is not guaranteed to be future-proof or as robust as the server-side validator.

Summary:
The recommended and most robust way is to use v.string() as the argument validator and then use ctx.db.normalizeId("table", id) inside your function to check validity at runtime. This allows you to handle errors gracefully and avoids uncatchable validation errors in your frontend [Discord: Value does not match validator].

If you need to check on the client before making the query, you would need to replicate the ID format check yourself, but this is not officially supported or recommended by Convex.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: