Excessive Deep Type Instantiation (TS2589)
Hey all, first of all - big fan of Convex, I'm about 10 days into learning it and it's been great most of the time. I've researched this quite a bit, and unfortunately, haven't found a magic bullet to fix this issue.
It appears to be quite a common issue for a lot of people and I understand why. But are there any best practices or or additional Cursor rules that would prevent this from happening?
I spent hours solving this in some files but not others and any minor change blows it up again. I haven't had my "AHA" moment yet where this truly starts making sense.
Any help would be appreciated!
44 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!
you can type cast the return type and it would disappear then,
Do you have an example? I've spent hours trying to fix this but no luck. Every time the project builds, I do something else and it blows up again
This seems to be a big issues for a lot of people by the looks of it (searched the Discord before)
I fail to understand how Convex is loved by LLMs, and yet this issue appears unavoidable in projects that do a little bit more 🙁
could you post the code you're dealing with?
yep, we appreciate the search effort!
also, this is not on convex's part, its a typescript issue that happens when type assert isn't fullfilfiled, if you google the error you will find mutliple solutions to this
To share a specific code, because I have tried for two days fixing this, and the only things that have worked are dynamic requires to break the chain. This feels super hacky. Unfortunately it is a bit of a bigger codebase so sharing specific pieces isn't really an option. I've tried a lot of things though, based on the docs, rules, and Discord messages. AI (GPT-5 + Sonnet 4.5, both) ran for hours at this point, trying all types of different things including experimential refactors, hah. No luck. Ultimately they all came back saying the same thing: typescript limitation, can't avoid, which I find hard to believe.
I wish there was a collection of best practices to avoid this particular problem (that is what I am trying to do) but none of this lead anywhere so far (all lead back to the require)
you can ts ignore it I think
Totally works in some cases, but not super scalable. My big problem is that I teach this stuff to non-technical people (currently teaching theam Supabase but really wanted to switch to Convex). And adding these quite out of the ordinary things like
ts-ignore or require() is not something that I can necessarily teach themthere's no specific solution to this without proper reproducible format, feel free to message me the code block that's causing the issue and I can take a look into it, I wonder if updating your typescript version would solve this
got it, thank you Sara. appreciate it, but while this would work for me, I was really hoping that Convex would be the no-brainer storage for 'vibe coders'
thanks for your help though
would you ever think this is acceptable? any negative consequences on the top of your head?

OK so in my particular app the problem was that there was too much business logic in the public actions. once this moved into an internal action, the problem was resolved. the question remains if there is a single source of all these 'can try' things
no that's not the issue, I think you need to keep it as is in the first line,
but! lucky for you this is a very popular issue that can be fixed by adding a return type to the internal function
https://docs.convex.dev/functions/actions#dealing-with-circular-type-inference
so either set a return type in the
returns or for the handler itselfthey all do already, but thanks again Sara
appreciate your help - wild to see that community support is so much better than paid support elsewhere
are you throwing shade or something? 🤣
am dead serious
getting support from a lot of companies these days is virtually impossible. meanwhile in convex there's a random mod answering near instantly
appreciate you
I tend to answer when I have an answer, most welcome!
@Sara and anyone else who runs into this in the future: I haven't tested this yet (leaving this here mostly for the AI to pick up), but luckily someone on X pointed me towards your new
Using static code generation (beta)
https://docs.convex.dev/production/project-configuration#using-static-code-generation-beta
This is very interesting because this was exactly the solution that I thought Convex should implement.
It is obviously not perfect, but it should, in theory, even though I haven't tried this yet, completely solve the excessive deep type instantiation entirely.
This should most likely be the default if it truly works, which again I'll try today. And if you do build with AI, this is exactly what AI wants to do. It constantly wants to write small light APIs for the massive object that contains Generate.
This might be controversial within the Convex team, but you guys should really stop claiming that this is slowing down large code bases or only relevant for code bases that are shared between repositories, or something like that, because the project that I tried it in is in no way, shape, or form a large project, and yet wasted four days on this.
Project Configuration | Convex Developer Hub
Configure your Convex project for development and production deployment using convex.json, environment variables, and deployment settings.
I really hope that this works, that it will help someone in the future and that you turn this on by default or at least make it an option when initiating a new Convex project or linking it, because this is clearly a big problem looking at the Discord.
Especially when building with AI
Yeah it makes sense that it fixes it, because it generates types for internals, good stuff dude!
I wish I could take credit for it, but I hope this will be more promoted. Because with this, if this fixes it (which as you say, it should), then Convex is quickly my number one tool zero doubt about it.
will lobby for this until my dying breath 🤣
haha, you'll deffently be noticed by the kapa.ai in the docs
what's your youtube channel?
https://www.youtube.com/@itsbyrobin
The reason I hammer this so much is because I think Convex has the tools and potential to be an incredible choice, even for non-technical people, if it is explained well and the process is straightforward enough.
I care a lot about making the process as straightforward as possible, and Convex has the ability to do this. However, if the static function stuff isn't promoted more, non-technical people will forget or skip over it and eventually run into this issue. We'll figure it out.
I never watch tech related videos ever, so this is gonna be hard 😂 , thank you for drilling this
it's all I watch 🤣
and sure, thanks for the support
@Wayne Can he post videos in #show-and-tell if he does a convex video?
Thanks for all of the breakdown in the thread!
Can you confirm whether the primary issue you're looking to address here is still circular type inference errors?
The problem with static generation is you lose inference entirely and have to provide a return validator for every function. So it "fixes" circular inference by just turning off inference entirely. The reference to this being mostly for larger codebases is that it's mostly meant to fix performance problems w/ types that show up as codebases grow.
My pleasure. I really like Convex and I would love to make it work. It feels like this is replacing one problem with another, but might be the lesser of the two evils. The main problem was indeed the excessive deep type instantiation TS2589. It took me 4 days to figure this out, and took major refactoring of the codebase to adhere to strict rules of separation (between api and internal). I doubt that this can reliably be enforced with e.g. Cursor rules at this point.
yes
I just tried this solution and it completely broke the entire codebase
but can confirm it did infact fix the excessibe deep instatation issue
I'm about to try it because my second project (a simple dashboard with youtube + dub.co integration to show stats for my business) and I ran into this after a few hours. This appears inevitable for anything but a small app, despite the docs frequently saying "for big projects" - maybe 'big' needs to be redfined in 2025 and AI coding...

it did indeed fix it for this project
I don't like this solution, I'll try my best to dig through it though
@Rob great thread and follow-ups.
When you say big codebase, ballpark how big?
For reference, I have 190 Convex functions and 34k lines of typescript.
Literally no idea if this a lot or not. But I’ve managed to get this far without the circular type or excessively deep error.
Would be happy to help figure it out if you’re still curious about solving it
I have 609 functions, which is less than yesterday, 67 tables, and complex logic, but I've never experienced this issue, I ALWAYS annotate returns though.
Thanks y’all!
Id love to understand how much AI you use in your workflow, and how much attention you pay to any specific structure.
The kicker here based on what you and others have said must be the “vibe coding” aspect (which is the angle at which I look at this as I teach non-technical people - think “advanced vibe coders”).
whatever GPT-5/Sonnet 4.5 does, eventually leads to these errors. I’m on my phone right now but will check number of functions, but it won’t be nearly as many as you.
It’s possible that there is one blatantly obvious thing that I’m missing/didn’t ask AI to pay attention to or fix, that you are super aware of. Okay, so it turns out that I have around 338 exported functions across different functionality. My app isn't that big to me, but it has a lot of CRUD-style functionality across multiple entities like users, lessons, courses, invitations, etc. I was able to get around it without turning out the static code generation in this particular project, but only by enforcing some pretty strict rules. that's mainly a layered architecture with very strict separation of responsibilities. - pure business (
It’s possible that there is one blatantly obvious thing that I’m missing/didn’t ask AI to pay attention to or fix, that you are super aware of. Okay, so it turns out that I have around 338 exported functions across different functionality. My app isn't that big to me, but it has a lot of CRUD-style functionality across multiple entities like users, lessons, courses, invitations, etc. I was able to get around it without turning out the static code generation in this particular project, but only by enforcing some pretty strict rules. that's mainly a layered architecture with very strict separation of responsibilities. - pure business (
services.ts) that has strictly no ctx, query, etc
- internal api (internal.ts) to run internalQuery, internalMutation, and internalAction
- public api that runs query, mutation, actions
by enforcing this, I was able to eliminate all of this, but this isn't "LLM friendly" in any way (I tried the same rules in a new project and they unfortunately weren't strictly enforced)@Rob I have found that LLMs are not good at managing the deep type support used by Convex. Same goes for libraries like TanStack, etc.
In my experience, this error results from one, some, or many mis-typed or overly-broadly typed implementations. If you have one errant return value (e.g.
any or some LLM-contructed type when it should use a generated type) , this can cascade all the way through the project and confuse the bejesus out of the TS server. Or if there are varying return types (or a missing return branch) in a function, it can confuse things.
If all functions have a well defined return type, using generated types when available, this error will typically be resolved.
I don't think we/I can help much more without seeing some sample of the code to see if we can spot what's going on.
Note that some actions can't live in the same file as queries or mutations, specifically if they need or use the node runtimes (use node at the top of the file). If there are any actions that need the node runtime, they'll need to be in their own file, as they'll then get bundled completely separately in the final build.
There are also some gotchas when importing non-Convex functions across runtimes (eg a Convex runtime function (queries.ts) and Node runtime files (myActions.ts) can't import the same function from bar.ts if bar requires node.
I fully agree that the various types, functions, and runtimes are a lot to shoulder as a developer. I have seen the Convex team ship DX improvements like crazy though: better error messages, better typing, more utilities and new components that abstract more and more of the complexity away from us.
Also, to sanity the TS server, don't be shy to delete the _generated folder, let it regen, and restart the TS server at will.Really appreciate it @Tom Redman! I'll definitely keep using it for my own projects to hopefully figure things out and simplify things along the road. This, plus further DX improvements and maybe the static stuff (which LLM might be better at dealing with to be honest) might just be enough to start talking about it at some point.
FWIW, I really love the way Convex works. once it clicks, you don't want to let go of it
the fact that my webhooks don't need a tunnel anymore is reason alone 😂
😄
Well keep us updated on anything you discover! It's always helpeful and there's lots of people searching for issues here.
Oh one thing you could explore is the Chef product, which is open source, and has been prompted meticulously to enable it to create idiomatic, typed Convex apps. You can grab some of the prompts here and use them in your LLM instructions.
https://github.com/get-convex/chef
GitHub
GitHub - get-convex/chef: The only AI app builder that knows backend
The only AI app builder that knows backend. Contribute to get-convex/chef development by creating an account on GitHub.
oh very interesting
thank you!
@Rob do you use Claude Code? I created a skill that will basically prevent and solve any/all TS2589 when building with convex if you’d like it
just answered you on x, hah. would love to see it and how you solved it
Just for anyone who was following and may be dealing with this.
Here’s the X post I sent Rob:
https://x.com/elijah_bowie/status/1981664794672271778?s=46
and here’s the link to the CLAUDE skill if you don’t use Claude you can still read the markdowns and apply it to other IDEs
https://u.pcloud.link/publink/show?code=kZ90u95Zr7BmsSdrEWV37uiVMR06RXCFosqk
Elijah Bowie (@elijah_bowie)
@itsbyrobin Here’s a cloud link to the files required to upload the skill into Claude. The explanation is provided in the accompanying PDF file. Simply drop the zip file into Claude to install the skill.
https://t.co/cMl1XImsEL
X
pCloud
Convex TS2589 - Shared with pCloud
Keep all your files safe, access them on any device you own and share with just the right people. Create a free pCloud account!
someone pointed out yesterday that it could be from the tsconfig not taking the
@/convex/* path
https://discord.com/channels/1019350475847499849/1019350478817079338/1432072341659058326