callOnce loses Nuxt context after first await
I have a setup like this, where
quote
is a pinia store and it works
However as soon as I await for anything else inside callOnce
, like e.g.
Suddenly, the page errors out with a
[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables
.
at useNuxtApp (/home/ll3006/sample-project/node_modules/nuxt/dist/app/nuxt.js:248:13)
at useAPI (/home/ll3006/sample-project/composables/useAPI.ts:3:56)
at Proxy.loadQuote (/home/ll3006/sample-project/stores/quote.ts:7:49)
at Proxy.wrappedAction (/home/ll3006/sample-project/node_modules/pinia/dist/pinia.mjs:1379:26)
at /home/ll3006/sample-project/app.js:17:19)
at async callOnce (/home/ll3006/sample-project/node_modules/nuxt/dist/app/composables/once.js:25:3)
at async setup (/home/ll3006/sample-project/app.js:18:25)
Which looks to me like loadQuote
doesn't work as it can't access useNuxtApp
anymore.
useNuxtApp
is ultimately used to load the api endpoint from the runtimeConfig, through a custom $fetch
-like $api
plugin.
Using $fetch
and manually hardcoding the endpoit works as expected. The problem therefore lies in the fact that only the first await
seems to receive the nuxt context.
Why does this happen? What causes loadQuote
to lose access to the nuxt context? Can I work around this?5 Replies
Hi @LL3006. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
You're experiencing a common issue with async context in Nuxt. This happens because Nuxt (like Vue) loses its context after the first
await
statement in an async function.
Why This Happens
The issue you're encountering is related to how Nuxt handles its context in async functions. As explained in the documentation:
On top of that, you must use them synchronously - that is, you cannot use await
before calling a composable [...]
Vue and Nuxt Composables
When you add the sleep
function before loadQuote()
, the Nuxt context is lost after the first await
statement. This is a design limitation with composables in Nuxt and Vue.
The Technical Explanation
During component lifecycle, Nuxt tracks a temporary instance of nuxtApp
via a global variable and unsets it in the same tick. This is essential for server rendering to avoid cross-request state pollution. After an await
statement, the context is no longer available:
useNuxtApp - A Deeper Explanation of Context
Solutions
1. Use runWithContext
The most direct solution is to use runWithContext
to restore the Nuxt context:
useNuxtApp - runWithContext
2. Use navigation
mode for callOnce
Since Nuxt v3.15, callOnce
supports a navigation
mode that might help in your case:
callOnce - Usage
3. Enable experimental async context support
If you're using a recent version of Nuxt, you can try enabling the experimental native async context support:
This feature uses Node.js AsyncLocalStorage
to make async context available natively to any nested async composable without needing a transform or manual passing/calling with context.
Native Async Context
4. Restructure your code
Another approach is to avoid nested async calls or structure your code differently:
Note on Try/Catch Blocks
Be especially careful with try/catch blocks containing await
statements, as they can cause additional issues with context restoration. The Nuxt documentation specifically mentions this as a problematic area:
TheRemaining Issues I hope one of these solutions helps resolve your issue! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------unjs/unctx
transformation to automatically restore context seems buggy withtry/catch
statements containingawait
<@293794800979673088> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@kapa.ai I see. Are there downsides to using the experimental asyncContext config?
Let me check the knowledge base for you again. One moment! :books:
----------------------
There are a few potential downsides to using the experimental
asyncContext
feature in Nuxt:
1. Platform Limitations: The feature relies on Node.js AsyncLocalStorage
API, which means it currently only works fully in Node.js and Bun environments. As mentioned in the GitHub PR:
This feature is limited to Node.js for now but there is an TC39 proposal ongoing to support native context in other platforms.GitHub PR #20918 2. Experimental Status: As an experimental feature, it may have bugs, performance issues, or undergo API changes in future Nuxt versions. This could potentially cause breaking changes in your application when upgrading Nuxt. 3. First-level Composable Limitation: When using from
<script setup>
, you might still need to call the first-level composable with nuxtApp.runWithContext
since Vue itself doesn't make async context available:
When using fromGitHub PR #20918 4. Potential Performance Impact: Maintaining context across async boundaries could have a small performance overhead, though this is likely negligible in most applications. 5. Debugging Complexity: Issues related to async context can be harder to debug since they involve tracking state across asynchronous operations. Despite these potential downsides, the feature can significantly improve developer experience by eliminating the need to manually pass context or restructure code to accommodate Nuxt's context limitations. If you're working on a Node.js-based deployment and the benefits outweigh the risks for your use case, it might be worth trying. As with any experimental feature, I'd recommend testing thoroughly in a development environment before deploying to production. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------<script setup>
, we need to call first level composable withnuxtApp.runWithContext
since Vue itself does not make async context available or has async context support.
<@293794800979673088> Kapa.ai is still learning and improving, please let me know how I did by reacting below.