dotnet/runtime · error

coreclr_shutdown_2 failed - Error: 0x%08x

Error message

coreclr_shutdown_2 failed - Error: 0x%08x

What it means

Printed in corerun_shutdown() when coreclr_shutdown_2() returns a failing HRESULT. The managed Main already executed and produced an exit code; this is a best-effort shutdown failure. The host forces exit_code = -1 so the process reports failure even if Main returned success.

Source

Thrown at src/coreclr/hosts/corerun/corerun.cpp:689

    }
#endif // TARGET_WASI
    return final_exit_code;
#endif // TARGET_BROWSER
}

extern "C" int corerun_shutdown(int exit_code)
{
    coreclr_shutdown_2_ptr coreclr_shutdown2_func = nullptr;
    if (!try_get_export(coreclr_mod, "coreclr_shutdown_2", (void**)&coreclr_shutdown2_func))
    {
        return -1;
    }

    int latched_exit_code = 0;
    int result = coreclr_shutdown2_func(CurrentClrInstance, CurrentAppDomainId, &latched_exit_code);
    if (FAILED(result))
    {
        pal::fprintf(stderr, W("coreclr_shutdown_2 failed - Error: 0x%08x\n"), result);
        exit_code = -1;
    }

    if (exit_code != -1)
        exit_code = latched_exit_code;

    ::free((void*)s_core_libs_path);
    ::free((void*)s_core_root_path);
    return exit_code;
}

// Display the command line options
static void display_usage()
{
    pal::fprintf(
        stderr,
        W("USAGE: corerun [OPTIONS] assembly [ARGUMENTS]\n")
        W("\n")

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Treat shutdown failure as non-fatal for the user's result but investigate lingering managed threads or background work in Main.
  2. Ensure Main returns / awaits all tasks before exiting so no foreground threads block shutdown.
  3. Avoid double-shutdown (calling corerun_shutdown twice) — the runtime tears down once.
  4. If reproducible only under load, profile for threads that ignore cancellation.

Example fix

// before: Main leaves a foreground thread running
static void Main() { new Thread(() => while(true){}) { IsBackground=false }.Start(); }
// after: mark background or exit cleanly
static void Main() { using var cts = new CancellationTokenSource(); new Thread(Work){IsBackground=true}.Start(); cts.Cancel(); }
Defensive patterns

Strategy: validation

Validate before calling

// managed-side: ensure Main awaits all work and no foreground threads linger
static async Task Main()
{
    using var cts = new CancellationTokenSource();
    var t = Task.Run(() => Worker(cts.Token));
    cts.CancelAfter(TimeSpan.FromSeconds(30));
    await t; // do not return until work is done
}

Prevention

When it happens

Trigger: coreclr_shutdown2_func(CurrentClrInstance, CurrentAppDomainId, &latched_exit_code) returns FAILED. Common when: shutdown is called on an already-torn-down runtime, a managed thread refused to stop, or a graceful shutdown timed out.

Common situations: Managed code left foreground threads running; native shutdown called twice; a pinned/unloadable assembly blocked teardown; runtime was only partially initialized due to an earlier recoverable error.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/da234fe56bac7620. Report an issue: GitHub.