#Applications errors

16 messages · Page 1 of 1 (latest)

ashen drift
#

What is the suggested way for our application to throw expected application errors without having them be seen as exceptions? For example, if a user has invalid input or tries to log in with the wrong password, this isn't something that we want to be treating as an exception for our error reporting and logging tools. What's the best way to do this? Because right now even a failed login is being seen as a 500 exception.

digital hornetBOT
#

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

#

You have two main options for “expected” failures like invalid input or wrong password, and both avoid treating them as unexpected 500s in your own reporting.

1. Return a typed “error” result (no throw)

Have the function return a union type that encodes success vs failure, and don’t throw at all:

// Example: login mutation
export const login = mutation({
  args: { email: v.string(), password: v.string() },
  handler: async (ctx, args) => {
    const user = await getUserByEmail(ctx, args.email);
    if (!user) {
      return { ok: false as const, error: "USER_NOT_FOUND" as const };
    }
    if (!checkPassword(user, args.password)) {
      return { ok: false as const, error: "INVALID_PASSWORD" as const };
    }

    // success
    return { ok: true as const, userId: user._id };
  },
});

This pattern is explicitly documented: for expected failures you can “return different values,” e.g.:

Id<"users"> | { error: "EMAIL_ADDRESS_IN_USE" };

so the client must handle both success and error branches in a type-safe way.[Application errors]
No exception is thrown, so nothing is reported as a 500 unless there’s a true system / developer error.

2. Throw ConvexError for expected failures

If you prefer using exceptions for control flow (e.g. to bubble from helpers, or to abort a mutation transaction), use ConvexError with structured data:

import { ConvexError } from "convex/values";
#

export const login = mutation({
args: { email: v.string(), password: v.string() },
handler: async (ctx, args) => {
const user = await getUserByEmail(ctx, args.email);
if (!user || !checkPassword(user, args.password)) {
throw new ConvexError({
code: "INVALID_CREDENTIALS",
message: "Email or password is incorrect",
});
}
// ...
},
});


On the client, you distinguish application vs “real” errors:

```ts
import { ConvexError } from "convex/values";

try {
  await login({ email, password });
} catch (error) {
  if (error instanceof ConvexError) {
    const data = error.data as { code: string; message: string };
    // handle expected error in UI, *don’t* send to exception tooling
    showToast(data.message);
  } else {
    // developer/read-limit/internal error: send to Sentry, etc.
    reportError(error);
  }
}

Convex specifically recommends ConvexError for expected failures where you still want transactional rollback and structured data; these are called “application errors”.[Application errors] You can configure your error-reporting integration to only capture non‑ConvexError exceptions, or to treat ConvexError differently based on error.data.

Which should you use?

  • If you don’t want these paths to show up as exceptions at all in your tooling: prefer option 1 (return typed results).
  • If you like exception bubbling and automatic transaction aborts but want to avoid noisy “500s” in monitoring: use option 2 and filter ConvexError (or specific codes in error.data) out of your “unexpected error” reporting.
#

Both approaches are explicitly supported; the docs note that “If you have expected ways your functions might fail, you can either return different values or throw ConvexErrors.”[App errors overview; Application errors]

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

#

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

ashen drift
#

@digital hornet So if I've hooked Sentry up to my backend and I throw a Convex error, will Sentry see that as an exception similar to the other types of raw exceptions that we might encounter?

And secondly, there's no way for us to customize the response code for a Convex mutation or query, right?

digital hornetBOT
#

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

#

  1. ConvexError + Sentry

Convex’s exception reporting integration sends all function exceptions (query/mutation/action/httpAction) to Sentry; the docs and Discord threads don’t state any special-case filtering for ConvexError vs other Errors.[Exception reporting; Error handling]

So based on the available docs, you should assume that:

  • Throwing a ConvexError in a function will be treated as an exception and reported to Sentry just like other thrown errors.
  • If you want to avoid certain expected failures (like invalid credentials) being treated as “real exceptions” in Sentry, you either:
    • Return typed values instead of throwing (no exception, nothing to report),[Application errors] or
    • Catch the error on the client and only forward non‑ConvexError (or certain codes in error.data) to Sentry using your own Sentry client SDK. The built‑in Convex↔Sentry integration itself is not documented as configurable at that level.[Exception reporting]

The docs also explicitly say the built-in Sentry support “does not yet support the advanced customization provided by the Sentry SDK,” which implies you can’t, for example, plug in custom beforeSend logic server-side through Convex’s integration alone.[Exception reporting]

  1. Custom HTTP status codes for queries/mutations
#

Convex queries and mutations are RPC-style functions; the public API doesn’t expose any way to set a custom HTTP status code for their responses. The error-handling docs talk about what happens when they throw (promise rejection, optimistic update rollback, etc.) but never mention configuring HTTP codes.[Errors in mutations; Error handling]

The only place you can directly control HTTP status codes is in httpAction, where you return a Response object yourself:

return new Response("no!", { status: 400, statusText: "Missing Secret" });

as shown in the custom wrapper example.[Custom httpAction]

So, from the available information:

  • No, there’s no documented way to customize HTTP status codes for queries or mutations.
  • If you need specific HTTP semantics (e.g. 401 vs 400), that needs to be done via httpAction, not via standard Convex queries/mutations.

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

#

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

compact harbor
#

@ashen drift curious what you do to filter these as being "exceptions" for sentry

ashen drift
#

@compact harbor So far I've just been tanking the spam but eventually I need to redesign my endpoints so that they return error objects instead of throwing errors. I'm used to designing APIs that would throw custom errors and use HTTP status codes. That's why I originally built it to throw errors but I need to change the response type.

thick gulch
#

facing the same issue. on Sentry side, you can use advanced inbound filtering to filter out "non-error" exceptions, but it's only available on the $80/month plan, which is a little steep

compact harbor
#

yeahhh

#

I am an error-thrower and I plan to continue being one for life lol