Mutation error: `Returned promise will never resolve` caused by triggers
I've never seen this error before, and when i get rid of this trigger in my blocks mutation it goes away...
The original mutation contains some promise all statements that insert docs into the
blocks
table
Any ideas?
38 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!
also it took me a very long time to find that the issue was related to the triggers, so a better error log would be very helpful in the future
Hmm. This error indicates that there's a deadlock (which is why it's so opaque; as far as Convex knows, the function just never returned). It's probably caused by the lock Triggers uses to guard against parallel updates, but I don't see how it could happen. Can you share more of the code so I can try to repro?
sent you some more details through a dm!
btw is there a way to see the sync status of triggers in the dashboard? similar to regular functions
What do you mean sync status? Triggers run inline within the mutation. You can do a
console.log
if you want to know that it's runningAh sorry i mean seeing the source code that has been deployed for triggers
Getting the same error here:
The only trigger for this table is essentially the same as what I DM'd you before for the other tables
i should say both authenticatedMutation and internalMutation are wrapped by triggers
The only way for us to use aggregates robustly is in conjunction with triggers but this is blocking that so it would be great to understand how to fix this
I have the same error. The "funny" part is that even if I edit the code of the trigger to do nothing (no query nor mutations), I get this error:
Given I have another trigger that works and this trigger does nothing, I suspect this is something related to the mutation that happens within the 'users' table that is the issue (but it works well without the trigger)
Is there a chance one of you could publish a small repro to github / pastebin? This definitely sounds like a triggers bug, but I'm kind of stumped unless I can reproduce it myself
so far the only way i've found to repro the error is something like this (wrapping it twice)
ah right, i have wrapped both authenticatedMutation and internalMutation with triggers, and i think the issue occurs when i call an internal mutation from an auth one
how do i fix this best?
how are you calling the internal mutation from the auth one? with
ctx.runMutation
?Best Practices | Convex Developer Hub
Here's a collection of our recommendations on how best to use Convex to build
no just await _functionname
Calling mutations directly as functions is not supported, and custom functions is one of the reasons why
The two options are to refactor out the handler (as described in best practices) or to call ctx.runMutation, which has extra overhead
this is not the case with calling internal queries directly right?
Calling mutations directly as functions is not supportedis there a good way to catch this during development? idk if this is possible with eslint rules...
Calling internal queries directly is also not supported. doing so will not cause problems with Triggers, but there may be other problems
Eslint would be great, but I'm not sure how such a rule would work. We're looking into it. For triggers we can throw a better error https://github.com/get-convex/convex-helpers/pull/348 which would have caught it in your case.
GitHub
nice error on nested triggers by ldanilek Β· Pull Request #348 Β· get...
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
nice that's already very helpful, but for queries i've never run into any issues so it's hard to detect
i just want to make sure i understand correctly where the issue is: calling custom functions from custom functions in general? or only certain cases like mutations from mutations or queries from mutations, etc
and how can i quantify the overhead of ctx.runMutation / ctx.runQuery? is it mostly due to arg validation?
the issue is calling any convex function directly from another convex function.
ctx.runMutation / ctx.runQuery start up a new javascript environment, serde the args and return values, and do arg validation. We don't recommend using them unless you're writing a component. The real recommendation is https://docs.convex.dev/production/best-practices/#use-helper-functions-to-write-shared-code
Best Practices | Convex Developer Hub
Here's a collection of our recommendations on how best to use Convex to build
are there any docs that indicated calling a convex function directly as if it were a function would work? We're trying to track down such docs.
Donβt think so, it just felt like the most concise way to reuse code
TS errors would definitely help instantly break the habit
absolutely, i'm trying those out now π
For actions, sharing code via helper functions instead of using ctx.runAction reduces function calls and resource usage.I guess this shouldn't just be actions then in the docs
true, good call out
what about calling convex functions from triggers? I assume that's not any different
For a while I've been a little foggy on what such "helper functions" might look like, but I think I saw an example in a video posted to the Convex YT channel last night. Jamie walks through the recreation of a Pokemon-related app that was demonstrated on Theo's channel. Here's a link to the relevant part of the video where he mentions this helper (
updateTally
).
Having an example like this in the docs would be really helpful IMO.Convex
YouTube
Porting Theo's T3 Stack Roundest to Convex
In a recent video (https://www.youtube.com/watch?v=O-EWIlZW0mM), Theo Browne built a "rate the roundest Pokemon" app using five different stacks. In a shocking turn of events, Convex was not one of the five.
In this video, Convex co-founder Jamie Turner walks through what it took to port the tRPC version to Convex. Spoiler: not much. And we end...
thanks for sharing!
The docs have example helper functions
ensureTeamAdmin
and getCurrentUser
. How can we improve the example?I had to go digging to find where those examples were shown. Ironically they're right under the text that @David Alonso quoted, which comes from the "Best Practices" page under "Production". π«’
Admittedly I don't recall reading that page yet (I thought I had, but those functions don't look familiar), and I'm trying to figure out why.
First off, I guess I've seen the phrase "helper functions" enough that I thought that the text David quoted was from some other part of the docs that I had read.
That aside, I'm guessing that I missed it because my approach to docs is to read the first few pages/sections to get the core info that I need, then to refer to the rest on an as-needed basis; e.g. to brush up on specific features. For me, I'd love to see the whole "Best Practices" page surfaced higher in the docs hierarchy. It feels buried in its current location, plus I feel that a list of best practices is useful for all phases of development, not just production. If I'd seen those example functions earlier in my introduction to Convex, it would have been really helpful as I was mulling over how to solve certain problems.
makes sense. i think we're planning on reorganizing docs like this. (i also linked that section twice in this thread π )
Jamie's video is also a good example though. i watched it last night π
I also had a postmortem session with @Tom Redman where I learned a lot of tricks about presenting code more clearly. I'm pretty excited for the next video, which I think is going to be excellent
slowly figuring things out!
Direct link to the referenced code snippet: https://github.com/jamwt/1app5stacks-convex/blob/main/convex-version/convex/pokemon.ts#L26-L57
GitHub
1app5stacks-convex/convex-version/convex/pokemon.ts at main Β· jamwt...
Theo built the same app 5 times because he's dumb (Jamie built it a 6th time, also dumb) - jamwt/1app5stacks-convex
with
updateTally
being said helper functionMy bad. I've been mostly following the conversation in this thread, not necessarily following all links.
does this seem like a promising path? trying to decide how much time to invest on our side on eslint rules and other guards
Working on it, but it may take a while. The change is easy, but we want to see how many projects will be affected
Is there a way we could somehow opt into it then?
Not that i can think of -- unless you want a custom build of the convex package
Could calculating/populating your aggregates e.g. once every 24 hours work in your situation? That's what we are doing in our project. We have some aggregates on a table that runs very hot write-wise, so we decided to not attach an aggregate-updating trigger to that table due to the high write volume. Instead we just update our aggregates with new documents from the last 24 hours once nightly. Of course this only works if you don't need very real-time aggregates.