How to fix Cannot use 'await' in non-async function in TypeScript
This TypeScript/JavaScript error occurs when you try to use the await keyword inside a function that hasn't been declared as async. The await keyword can only be used within async functions or at the top level of modules in modern environments.
Quick fix
Fixes TS1308
// BEFORE - Error TS1308
function fetchData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}
// AFTER - Fixed by adding async
async function fetchData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}- Add the async keyword to your function
- Fix async callbacks in array methods
- Handle constructors and special contexts
- Enable top-level await if needed
// BEFORE - Error TS1308function fetchData() {const response = await fetch('https://api.example.com/data');return response.json();}// AFTER - Fixed by adding asyncasync function fetchData() {const response = await fetch('https://api.example.com/data');return response.json();}Cannot use 'await' in non-async function
What this error means
The `await` keyword is used to pause the execution of an async function until a Promise is resolved. It's a fundamental part of JavaScript's asynchronous programming model. However, `await` can only be used in specific contexts: inside async functions or at the top level of ES modules. When TypeScript encounters an `await` expression inside a regular (non-async) function, it throws error TS1308. This is because the JavaScript runtime needs the function to be marked as `async` to properly handle the Promise-based control flow that `await` creates. This error is extremely common for developers learning async/await patterns or when converting callback-based code to Promise-based code. The error occurs at compile time in TypeScript, but in plain JavaScript, it manifests as a syntax error that prevents the code from running at all. The `async` keyword transforms a function to always return a Promise and allows the use of `await` within its body. Without this keyword, the runtime has no mechanism to pause execution and wait for Promise resolution.
Typical symptoms
- TypeScript compilation fails with error TS1308
- Error message: 'await' expressions are only allowed within async functions and at the top levels of modules
- Code that worked in JavaScript breaks when migrating to TypeScript
- Your IDE shows a red squiggly line under the await keyword
- Runtime SyntaxError in plain JavaScript: 'await is only valid in async functions'
- Function appears to execute but await doesn't pause execution as expected
Common causes
- You forgot to add the async keyword to a function that uses await
- You're converting callback-based code to async/await but missed declaring the function as async
- You're trying to use await in a class constructor (which cannot be async)
- You're using await in a regular callback function (e.g., in forEach or map) without making it async
- You're attempting to use await at the top level of a script without module support
- You copied async code into a function without updating the function signature
- TypeScript configuration doesn't support top-level await (target/module settings)
How to fix "Cannot use 'await' in non-async function"
The most common fix is to simply add async before the function declaration:
// BEFORE - Error TS1308
function fetchData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}
// AFTER - Fixed by adding async
async function fetchData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}This works for all function types:
// Arrow function
const getData = async () => {
const result = await somePromise();
return result;
};
// Method in a class
class DataService {
async fetchUser(id: number) {
const user = await fetch(`/api/users/${id}`);
return user.json();
}
}
// Async IIFE (Immediately Invoked Function Expression)
(async () => {
const data = await fetchData();
console.log(data);
})();When using await inside array methods like forEach, map, or filter, you need to make the callback function async:
// WRONG - forEach doesn't handle async properly
items.forEach((item) => {
await processItem(item); // Error: await in non-async function
});
// WRONG - even with async, forEach ignores promises
items.forEach(async (item) => {
await processItem(item); // Runs all in parallel, doesn't wait
});
// CORRECT - Use for...of loop for sequential processing
for (const item of items) {
await processItem(item);
}
// CORRECT - Use Promise.all for parallel processing
await Promise.all(items.map(async (item) => {
return await processItem(item);
}));
// CORRECT - Use for await...of for async iterables
for await (const item of asyncIterable) {
console.log(item);
}The key difference: forEach doesn't wait for async callbacks to complete, while for...of properly handles await.
Constructors cannot be async, so you need alternative patterns:
// WRONG - constructors cannot be async
class UserService {
constructor() {
this.user = await this.fetchUser(); // Error!
}
}
// CORRECT - Use a static factory method
class UserService {
private constructor(private user: User) {}
static async create() {
const user = await this.fetchUser();
return new UserService(user);
}
private static async fetchUser() {
const response = await fetch('/api/user');
return response.json();
}
}
// Usage
const service = await UserService.create();For React components (which can't be async):
// WRONG - React components can't be async
async function MyComponent() {
const data = await fetchData(); // Error!
return <div>{data}</div>;
}
// CORRECT - Use useEffect hook
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
async function loadData() {
const result = await fetchData();
setData(result);
}
loadData();
}, []);
return <div>{data}</div>;
}Modern JavaScript/TypeScript supports top-level await in modules. Update your tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "esnext",
"lib": ["ES2022"]
}
}Then you can use await at the top level:
// In a module (.ts or .mjs file)
const config = await fetch('/api/config').then(r => r.json());
const db = await connectDatabase(config);
export { db };Requirements for top-level await:
- TypeScript 3.8+
- module set to esnext or es2022
- target set to es2017 or higher
- Code must be in a module (has import/export statements)
Note: Top-level await blocks module execution until the Promise resolves, so use it sparingly.
If you absolutely cannot make a function async, use Promise chains with .then():
// When async is not an option
function getData() {
// Can't use: const result = await fetch('/api/data');
// Use .then() instead
return fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log(data);
return data;
})
.catch(error => {
console.error(error);
throw error;
});
}
// Usage
getData().then(result => {
console.log(result);
});However, this is usually a sign you should make the function async instead. Promise chains are harder to read and maintain than async/await.
Advanced notes
### Understanding Async Function Behavior
When you declare a function as async, TypeScript/JavaScript automatically wraps its return value in a Promise:
// These two are equivalent
async function getValue() {
return 42;
}
function getValue() {
return Promise.resolve(42);
}
// Both must be awaited
const value = await getValue(); // 42### Error Handling in Async Functions
Always handle errors in async functions, especially when using await:
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch data:', error);
throw error; // Re-throw or handle gracefully
}
}### Async/Await vs Promise.all
For parallel operations, use Promise.all instead of sequential awaits:
// SLOW - Sequential (3 seconds total if each takes 1s)
const user = await fetchUser();
const posts = await fetchPosts();
const comments = await fetchComments();
// FAST - Parallel (1 second total)
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);### TypeScript Module vs Script Mode
If top-level await doesn't work, ensure your file is treated as a module:
// Add any import or export to make it a module
export {};
// Now top-level await works
const data = await fetchData();Without any import/export statements, TypeScript treats the file as a script, not a module, and top-level await is not allowed.
### Async Functions Return Promises
Remember that calling an async function always returns a Promise immediately:
async function doWork() {
await someTask();
return "done";
}
// Returns a Promise<string>, not a string
const result = doWork(); // Promise { <pending> }
// Must await to get the actual value
const actualResult = await doWork(); // "done"This is why forgetting await when calling async functions is a common bug—you get a Promise instead of the resolved value.
Frequently asked questions
- What does "TS1308" mean?
- The await keyword is used to pause the execution of an async function until a Promise is resolved. It's a fundamental part of JavaScript's asynchronous programming model. However, await can only be used in specific contexts: inside async functions or at the top level of ES modules.
- How do I fix "TS1308"?
- This TypeScript/JavaScript error occurs when you try to use the await keyword inside a function that hasn't been declared as async. The await keyword can only be used within async functions or at the top level of modules in modern environments.
- What causes "TS1308"?
- You forgot to add the async keyword to a function that uses await You're converting callback-based code to async/await but missed declaring the function as async You're trying to use await in a class constructor (which cannot be async)
- Is the "TS1308" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚TypeScript Official Documentation
- 📖MDN - async function(Official Docs)
- 📖MDN - await operator(Official Docs)
- 📖TypeScript 3.8 - Top-Level Await(Official Docs)
- 🐙