DEV Community

Cover image for Why Your .NET Application Works in an IDE but Fails in CI/CD or Terminal: Understanding Asynchronous Execution
Laura Pučkoriūtė
Laura Pučkoriūtė

Posted on

Why Your .NET Application Works in an IDE but Fails in CI/CD or Terminal: Understanding Asynchronous Execution

Yup, I missed an await .

Nothing new. No one else to blame.

But why did it take so long to find it? Because the app worked locally but failed on the pipeline, I thought it was a pipeline problem! Is the Azure pipeline set up in some way that doesn’t wait for my dotnet run command to finish? Does an error occur that is not properly logged and therefore not visible on the pipeline run? Do I need to use a different Azure DotNetCoreCLI task or instead start the program with a shell script? Am I losing my mind?

Asynchronous programming can be tricky, especially when tasks are not awaited properly. This often leads to unexpected behavior, especially when running applications in different environments (like Rider or other IDEs vs. the terminal or CI/CD pipelines).

My case

static void Main(string[] args)
{
    SomeProcess(); // Missing await
}

static async Task SomeProcess() {
    await SomeOtherProcess();
}
Enter fullscreen mode Exit fullscreen mode

My Main function called a process that had a few asynchronous tasks. On the pipeline, observing the logs, I saw that the pipeline task running the program would be completed at random times, rarely letting the .NET program complete a full run.

That is because the call in the Main function to a process initiating asynchronous tasks never waited for that process to complete, and the Main function run finished whenever it pleased.

But why did the .NET program always run fully in the Rider IDE?

Seems like Rider might have a specific run environment. Rider might implicitly handle unawaited tasks, potentially waiting for asynchronous tasks to complete before terminating the run process, allowing the app to seem to run fully. An IDE might introduce additional debugging or process-wait mechanisms that make the application appear to be more forgiving in certain cases.

When running in the terminal or CI/CD pipelines, there is no "help" from Rider or other IDEs. The application exits immediately after the Main method finishes, even if tasks are still running in the background. Without await, these background tasks won’t finish before the application ends, leading to errors or incomplete operations.

How I fixed it

First, it was important to start my application by running the dotnet run command from the terminal, and not an IDE. This way it was clear that the problem was in the application code, not in my pipeline setup.

Then, I had to dig a bit through the code to find where exactly I was not handling the asynchronous processes properly. Once I found it, I added the missing await statement. And it worked like magic.

static async Task Main(string[] args)
{
    await SomeProcess();
}

static async Task SomeProcess() {
    await SomeOtherProcess();
}
Enter fullscreen mode Exit fullscreen mode

Lessons learned

  • Always await asynchronous operations.
  • Test in multiple environments: test the run of your application in a terminal or similar environment to where it is failing.
  • Do not rely just on the IDE run processes to verify the functionality of your program.

Top comments (0)