When inspecting your logs, you may occasionally see output from different requests mixed together under a single request. This guide explains why this behavior occurs and provides robust solutions to ensure your logs remain consistent.
This behavior is not a platform bug but a characteristic of how asynchronous code executes in serverless environments. When a Vercel Function handles a request, and your code initiates an asynchronous task (like an API call or database query), but does not await
its completion, the Function may send its response and freeze its execution context before the task is finished.
Here's the sequence of events that leads to mixed logs:
- Task Initiated: A Function receives Request A and starts an asynchronous task without awaiting it.
- Response Sent: The Function sends a response for Request A and the execution environment is "frozen" to conserve resources.
- Task Orphaned: The un-awaited task remains pending in the frozen environment.
- Environment Reused: A new, unrelated Request B arrives. To optimize performance, the same warm environment is "thawed" and reused.
- Task Completes: The orphaned task from Request A finally completes and logs its output. Because the active context is now Request B, the logs are associated with Request B's ID.
This creates the illusion that logs are being incorrectly grouped, when in fact they are being attributed to the request that is active when the log is written. An interactive version is available here.
You can confirm if this is happening by looking for these patterns in your Runtime Logs:
- Mixed Identifiers: A single request log contains unique identifiers (like
userId
orcartId
) from multiple different users or sessions. - Anomalous Durations: A logged operation shows a duration that is much longer than the total execution time of the request it's associated with.
- "Fire-and-forget" Code: Your Function code contains asynchronous calls that are intentionally not
await
ed.
To resolve this, you must ensure that your asynchronous tasks are correctly managed within the Function's lifecycle.
Solution 1: Await tasks that are critical for the response
For any task whose result is needed to build the response, you must await
its completion. This guarantees that all necessary work is finished within the context of the correct request. Using Promise.all()
is an efficient way to handle multiple parallel tasks.

Solution 2: Use helpers for tasks that can run after the response
For non-critical tasks like logging or sending analytics, awaiting them would needlessly delay the response. For these scenarios, you should use a helper method that allows the task to complete in the background after the response has been sent.
- For Vercel Functions (Edge and Node.js) Use the
waitUntil()
helper from the@vercel/functions
package to enqueue a task that runs after the response. For more information, see thewaitUntil()
API Reference

- For the Next.js App Router Use the experimental
after()
API to run code after the response has finished streaming. For more information, see the Next.jsafter()
API Reference.

Note: Tasks handled by
waitUntil()
andafter()
are still bound by the Function's maximum execution timeout. If the Function times out, these background tasks will be terminated.