JavaScript
async functions
await
syntax error
error handling

How to resolve the Syntax error await is only valid in async function?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The error "await is only valid in async function" means JavaScript found an await in a place where the language grammar does not allow it. The fix is usually simple, but the correct solution depends on whether the code is inside a normal function, an event callback, or top-level module code.

Mark the function as async

The most common case is using await inside a function that was never marked async:

javascript
1function loadUser() {
2  const response = await fetch("/api/user");
3  return response.json();
4}

That fails because await suspends only async execution contexts. Add async to the function:

javascript
1async function loadUser() {
2  const response = await fetch("/api/user");
3  return response.json();
4}

Now loadUser() returns a promise automatically, which means callers must either await it or handle the promise with .then().

Fix nested callbacks and array helpers

Another frequent trap is putting await inside a callback that is still synchronous, even if the outer function is async. For example:

javascript
1async function loadAll(ids) {
2  ids.forEach((id) => {
3    const response = await fetch(`/api/users/${id}`);
4    console.log(await response.json());
5  });
6}

The outer function is async, but the forEach callback is not. You have two clean fixes. The simplest is a normal loop:

javascript
1async function loadAll(ids) {
2  for (const id of ids) {
3    const response = await fetch(`/api/users/${id}`);
4    console.log(await response.json());
5  }
6}

If you want parallel requests, use Promise.all:

javascript
1async function loadAll(ids) {
2  const users = await Promise.all(
3    ids.map(async (id) => {
4      const response = await fetch(`/api/users/${id}`);
5      return response.json();
6    })
7  );
8
9  console.log(users);
10}

This pattern is important because many JavaScript bugs come from assuming await "inherits" async behavior from an outer scope. It does not. The exact function containing await must be async.

Use top-level await only in modules

Modern JavaScript allows top-level await, but only in ES modules. This is valid:

javascript
const response = await fetch("https://example.com/data.json");
const data = await response.json();
console.log(data);

But it works only if the file runs as a module. In Node.js that usually means using an .mjs file or a package configured for ESM. In the browser, it means a module script:

html
<script type="module" src="./app.js"></script>

If your environment is not a module, wrap the code in an async function instead:

javascript
1async function main() {
2  const response = await fetch("https://example.com/data.json");
3  const data = await response.json();
4  console.log(data);
5}
6
7main().catch(console.error);

If you cannot use async, fall back to promises

Some APIs expect a synchronous callback, or you may be working in older code where promise chains fit better. In those cases, replace await with .then():

javascript
1function loadUser() {
2  return fetch("/api/user")
3    .then((response) => response.json())
4    .then((user) => {
5      console.log(user);
6      return user;
7    });
8}

This is not more modern, but it is still correct. The important part is matching the async style to the place where the code runs.

Common Pitfalls

The most common mistake is adding await inside forEach, filter, or other callbacks without marking those callbacks async or without reworking the flow. In many cases, a for...of loop is simpler and more correct.

Another pitfall is forgetting that async functions always return promises. After fixing the syntax error, the next bug is often a caller that expects a plain value.

Top-level await also causes confusion. It is valid only in module contexts, not in every JavaScript file by default.

Finally, make sure the thing you await is actually a promise or thenable. await on a non-promise value is legal, but it may hide a logic mistake where the asynchronous operation never existed in the first place.

Summary

  • Put await only inside an async function or at top level in an ES module.
  • Check nested callbacks carefully because outer async functions do not make inner callbacks async.
  • Prefer for...of or Promise.all over forEach when asynchronous work is involved.
  • Use top-level await only when your runtime treats the file as a module.
  • Fall back to promise chains when an async function is not the right tool for the surrounding API.

Course illustration
Course illustration

All Rights Reserved.