error CS0029: Cannot implicitly convert type 'System.Threading.Tasks.Task<int>' to 'int'
means you called an async method and forgot to await it. The
method hands back a Task<int>, a promise of a number, and you tried to
store it in an int. Put await in front of the call.
The one-word fix is int count = await CountLinesAsync("notes.txt");. The
method that contains that line must itself be able to await: top-level statements can,
an ordinary method needs the async keyword and a Task return type.
The error
...\Program.cs(1,13): error CS0029: Cannot implicitly convert type 'System.Threading.Tasks.Task<int>' to 'int' [...\FixLab.csproj]
Build FAILED.
Only the folder part of the two paths was shortened; the rest is as
dotnet build printed it.
Why it happens
This is the code that produced it, a small console program that counts the lines of a text file with the asynchronous file API:
int count = CountLinesAsync("notes.txt");
Console.WriteLine($"Lines: {count}");
static async Task<int> CountLinesAsync(string path)
{
string[] lines = await File.ReadAllLinesAsync(path);
return lines.Length;
}
The method says it returns int in its body (return lines.Length;),
but because it is marked async its real return type is
Task<int>. Calling it starts the work and gives you the task straight
away, before the file has been read. A task is not a number, and C# has no implicit
conversion between the two, so the compiler stops at column 13 of line 1, the call itself.
The message names the full type, System.Threading.Tasks.Task<int>,
which is the clue: whenever you see Task<T> where you expected
T, an await is missing.
The fix
int count = await CountLinesAsync("notes.txt");
Console.WriteLine($"Lines: {count}");
static async Task<int> CountLinesAsync(string path)
{
string[] lines = await File.ReadAllLinesAsync(path);
return lines.Length;
}
With await the build succeeded with no warnings and the program printed
Lines: 3 for a three-line file. If the call sits inside a regular method,
mark that method async Task (or async Task<int>) and await
it from its caller too; async spreads up the call chain, and that is normal.
The tempting shortcut is CountLinesAsync("notes.txt").Result. It compiles,
but it blocks the thread while it waits, and a failure arrives wrapped: in a side test a
FileNotFoundException thrown inside the method reached the caller of
.Result as an AggregateException. Use it only when you truly
cannot make the caller async.
How it was reproduced
A new project from dotnet new console on .NET SDK 10.0.403 (net10.0, nullable
and implicit usings on, no packages), with the program above in Program.cs.
dotnet build failed with the single CS0029 error; after adding
await, dotnet build --no-incremental -p:TreatWarningsAsErrors=true
succeeded and dotnet run printed the line count.
Frequently asked
- How do I convert Task<int> to int in C#?
- Await the task: int value = await GetValueAsync(); The method containing that line must be async or be top-level statements. Avoid .Result unless the caller cannot be made async, because it blocks the thread.
- Why does my async method return a Task instead of the value?
- An async method returns as soon as it reaches its first unfinished await, so it hands back a Task that will hold the value later. The await keyword unwraps that Task into the value once the work completes.
- Is .Result the same as await?
- No. Both give you the value, but .Result blocks the calling thread until the task finishes and wraps exceptions: a FileNotFoundException came out of .Result as an AggregateException, while await rethrew the FileNotFoundException itself.
For the idea behind tasks and await, read
async and await explained simply. More
decoded errors are in the Fixes category.