runAfter is not throwing an error on the client
Hello, can't understand why error is not throwing to the client.
This is the pattern I'm trying to implement.
I can see the Error 'Called getCurrentUser without authentication present' logged in convex, but I can't get it in the front end. I'm using Next.js.
convex/customers.ts
convex/customers_usenode.ts
convex/facturapi_usenode.ts
76 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!
convex/organizations.ts
convex/users.ts
You just throw error in your convex functions and catch them in your frontend.
I think I'm throwing them, aren't I?
where do you call those convex function in the frontend
I have a form, this is the onSubmit function
What is upsertCustomer
No matter the backend is throwing an error, I always get the toast with 'Cliente creado' (customer created).
What is upsertCustomer ?
upsertCustomer is create a customer if it doesnt exist or update the customer if exist
i know, where do you define it???
const upsertCustomer = useMutation(....????
const upsertCustomer = useMutation(api.customers.upsertCustomer)
I define it at the begining of the component
Thank you
Change this:
and test your frontend now, and see if you get an error π
This way you validate that the backend can indeed throw error.
Ok
I will try that noe
now
This code is wrong:
Yes, thats why I saw that all the code in runAfter is not throwing any Error.
Do you know how can I solve this?
well if those are indeed schedules, then you need to create additional table where you save those ids, and after that update the frontend with the results...if error you show RED notification, or green depending on when it finishs.
im not sure what you trying to accomplish here.
I'm still trying to figure it out the convex patterns and architecture. The only solution I found to run an action from a mutation is the runAfter.
What is upsertCustomerWithFacturapi doing? calling third party api?
Im running that action conditionally
Yes
If (hasRequiredFacturapiFields) --> calls action (external api)
if not --> create a mutation in convex
so you want to show failure in case the third party api fails ?
Yes!
api.customers_usenode.upsertCustomerWithFacturapi
where is this code
The error is inside
const facturapi = await getFacturapiLiveInstance({ ctx })
Then I'm calling here getOrganizationFacturapiKeys
Then I'm calling getCurrentOrganization
here
Then I'm calling getCurrentUser
and inside here is where I see the error in convexyou define upsertCustomerWithFacturapi as action, but calling it directly from mutation function, which you should not do.
the way the functions should call each other is through ctx.runAction, ctx.runMutation, or you are not following convex rules.
In convex I see this error string
Called getCurrentUser without authentication present
yes how did you call getCurrentUser? from which function π
From
getCurrentOrganization
you should NOT call other functions directly, you must use ctx.runAction, ctx.runMutation, ctx.runQuery, etc.
the only time you call other functions is when you know what you are doing π
Inside a mutation I dont have access to runAction
of course, because mutation should not have sideeffects,, it should not call third party π
you can do it upfront in the frontend code
Ah ok, I get it
So, in the frontend I can call the mutation or the action conditionally?
no when you call mutation its only if you need to insert something DIRECTLY without calling any third part api.
if you need to call third party api, you must call "ACTION" => GETAPI > MUTATION
I need to call the api conditionally, so is ok this?
the action should not be internal...
Note: In most cases calling an action directly from a client is an anti-pattern. Instead, have the client call a mutation which captures the user intent by writing into the database and then schedules an action
because the frontend need to call it
because you are using UseMutation in the frontend, which is incorrect
const get = useAction(...);
Yes, I understand the useAction, but, I have some validation, If there is the presence of some needed fields, I need to make the api call, if not, I only need to make a mutation.
Thats why I'm thinking of this istead
action functions can have validation args
But then I see this
Note: In most cases calling an action directly from a client is an anti-pattern. Instead, have the client call a mutation which captures the user intent by writing into the database and then schedules an action
and I don't know if my situation if most of the cases
Validation I mean in the frontend, because the api does not validate if I don't send some required fields, but, I want to save the record of the user for later update, validate and use
For example if the user send me the field alias, I only save in convex, but if sends... alias, legalName, taxId, taxSystem and zip --> Then I call the apiim not sure where you get those note from.
Convex docs
Actions | Convex Developer Hub
Actions can call third party services to do things such as processing a payment
If you want your schedule to run as background process, then you do it like that. but you dont know in the frontend if its success or failure
@dannyelo instead of directly calling actions from mutations, you should handle third-party API calls in actions. Move your conditional API logic to an action, and have the mutation schedule this action using ctx.scheduler.runAfter. This way, you follow the correct Convex pattern:
mutation > schedule action > action handles API and writes to DB.
To handle conditional API calls, use an action for calling third-party APIs, not a mutation. In your case, validate the required fields in the action, and if the fields are incomplete, skip the API call and handle the database mutation separately. Avoid directly calling actions from mutationsβuse ctx.runAction to follow Convex patterns.
@Hmza @dannyelo want the errors from the 3rd party api to the frontend, thats the whole purpose of this question. If he use schedule, he cant get the errors, if he use actions, then its possible.
@Hmza Thank you, what @jamalsoueidan is saying is a good point, am I going to be able to catch errors with the scheduler method?
If you need to catch errors in real-time from the third-party API, use an action and call it directly from the frontend via
useAction
. The scheduler is for background tasks and won't return immediate errors to the client.
Actions will allow you to handle success or failure directly in the frontend.
https://docs.convex.dev/functions/actionsActions | Convex Developer Hub
Actions can call third party services to do things such as processing a payment
@Hmza And what about calling an Action form the frontend anti-pattern?
Calling an action directly from the frontend can be considered an anti-pattern if you're not capturing the user intent or state in the database.
To follow best practices, you should use a mutation to capture intent, store relevant data, and then call the action from the backend.
But if your main goal is to handle
third-party API
calls and return errors in real-time
, calling the action directly is acceptable in this case.re best practice, it's nice in case you want to set up retrying of the action (if things in the action like fetch calls are likely to fail) but if it's something that users can retry themselves and you show errors states for on the client, or you just aren't worried about retries, this works well
@Hmza Ok got it, so if I want to catch errors, whether is a mutation or an action, I will have to manage the logic in the client?
Something like this?
This way I avoid running scheduler methods
yes this will work well enough for your use case!
Great! I 'll try this then!
Thank you @jamalsoueidan @Hmza @ballingt π
You do not use two methods for the same thing, thats incorrect. You always use one method and in the backend you handle both cases (with api, without api), and in the frontend you catch the error.
@jamalsoueidan That also makes sense
what data you getting from the 3rd party api?
I'm posting data, not fetching
ahh you are posting to third party ok
If the post returns a success response, I know everything is ok
okay perfect...then you just use action and make a condition, ctx.runAction(... and then if its success you do the mutation
but do not call functions directly...
Yes, I'll try that. What do you mean with: but do not call functions directly...?
After I pass the logic to the Action, I'm getting these weird type errors.
'upsertCustomerAction' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)
'handler' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.ts(7023)
'organization' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)
that looks much better π
I was returning the runAction, I remove it and solve the problem
Yes!
can you show me your sourcecode...so i have better overview of the whole base π
What you do you mean of showing my source?
How can I do that?
You want to see the whole repository?
exactly!
Of course, itβs a private repo on GitHub
@jamalsoueidan The implementation is working great now, thanks again!
@dannyelo im glad you get it to work, remember to always use ctx.runXXX π
Yes I will!
I have a rather complex event management system that has several action mutation/scheduler combinations with error log/capture/display. I was using actions to capture user intent and errors but switched over to mutations in a try catch, it works most of the time but now certain valdator errors escape my front end try catch. and user intent is lost. Actions i don't recall having that issue. From my understanding one could modify the client to avoid your mutation in catch statement. So a properly written action is higher security in a way? (My justification for keeping the action code)
I just havent had the time to debug the try catch nextjs.. as its like 99% of the stuff catches right.. any suggestions?
hmm, It might just be simple as my error expecting a valid payload π all because i want to be able to replay the event. Is there a way to do a validation check within a mutation without throwing?
A properly written mutation can be just as secure as an action. The key is to implement proper validation and error handling. Mutations run transactionally, which can provide better data consistency guarantees. to answer your question about the validator errors escaping frontend try/catch you should use
ConvexError
for structured errors (throw in convex and catch it on client side)
https://docs.convex.dev/functions/error-handling
//mutation
//client
you can also handle without throwing in mutation itself
for replying event you can use a sourcing pattern that stores your raw events in db and then mutation are run on those to make sure you are processing them right.
finally for your 1% problem of errors it might be worth adding more detailed logging or using a debugging tool to trace the error propagation. You could also consider using a global error boundary in React to catch any errors that escape local try/catch blocks.Error Handling | Convex Developer Hub
There are four reasons why your Convex