Query/Mutation Error Handling & Transactional Integrity Advice
Hey Convex team and community!
We're working on improving the developer experience and type safety of our convex API by standardizing our response types, and we'd love your advice on the idiomatic "Convex way" to approach this. Our goal is to move away from implicitly throwing errors for business logic failures (e.g., "Document not found," "Permission denied") and toward a more explicit API contract.
Our initial idea was to adopt a
Result
type for all our queries and mutations, like Promise<Result<SuccessData, AppError>>
. This would make our function signatures self-documenting and allow for type-safe error handling on the client without try/catch
boilerplate for every call.
However, we then realized a critical detail: throwing an Error
is fundamental to how Convex rolls back transactions in mutations. If we stop throwing and instead return an error object (e.g., { status: 'error', ... }
), we're concerned we'll lose the automatic ACID guarantees for our failure paths, which is one of the features we love most about Convex.
So, our question for the team is:
1. What is the recommended pattern in Convex for returning structured, typed errors from queries and mutations?
2. Is there a way to use a Result
-like pattern without breaking transactional rollbacks? For instance, is there a way to manually cancel a transaction within a mutation before returning the error object?
3. Or, is the best practice to create a custom AppError extends Error
class, throw
it from our functions, and then use type guards on the client to inspect the error caught in a try/catch
block?
Any insight into best practices here would be super helpful. Thanks so much8 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
Bump: Also very interested in hearing people's ideas on this :)) both from Convex team and regular mortals
++ I would really appreciate input as well
Have you read this? https://docs.convex.dev/functions/error-handling/application-errors
Application Errors | Convex Developer Hub
If you have expected ways your functions might fail, you can either return
It sounds like you want to throw ConvexError which can have structured data
Yes, we currently do this. I guess my core concern is not really a convex problem but more of a typescript one. Errors are not apart of a function return type in typescript and the client as no way to know what type of errors are thrown. I think the best solution is to create a catchAll hook on the client that wraps our querys and mutations and we can parse the structured data and handle it.
@Jamal I exactly have the same problem as you.
After having worked with Convex for more than a year, this is still the part I am not sure what is the best pattern.
I think it would be nice to somehow be able to do call it that way the frontend:
api.function._errors
that is an exported object that would need to be definedYeah, I wish there was a way to programmatically cancel the database transaction then it would be possible to use errors as values and not throw them.