0XC0190060

STATUS_EXPIRED_HANDLE (0xC0190060) Fix for SQL Server & NTFS

Database Errors Advanced 👁 0 views 📅 May 28, 2026

Your transaction handle expired because SQL Server or NTFS lost track of it mid-commit. Rollback the transaction and use a shorter transaction scope.

Quick answer

Rollback the current transaction, then reduce the transaction scope length or disable implicit transactions in your connection string (Implicit Transactions=False).

This error—STATUS_EXPIRED_HANDLE (0xC0190060)—happens when Windows (usually NTFS or SQL Server's own lock manager) invalidates a transaction handle because it thinks the transaction went stale. I've seen this most often in high-concurrency SQL Server environments (especially SQL Server 2019 on Windows Server 2022) where a SqlTransaction or TransactionScope was left open for more than a few seconds without any activity. The underlying kernel handle expires, and SQL Server can't commit or rollback cleanly. You get the error, and your app throws an unhandled SqlException.

Why this happens

The handle expiration is a protection mechanism built into Windows (since NT 6.0). If a transaction handle isn't used for a certain period (default timeout is 60 seconds for TransactionScope in .NET, but the kernel-level handle timeout is shorter—30 seconds in some NTFS configurations), the kernel marks it as expired. SQL Server's lock manager relies on these handles for distributed transactions or file-backed operations. When the handle expires, SQL Server can't complete the commit or rollback because the kernel won't honor the handle anymore. This isn't a SQL Server bug—it's a design constraint. You have to keep your transactions short and commit promptly.

Fix steps

  1. Identify the offending transaction
    Look in your application logs for the exact call stack. The error usually surfaces in a try-catch around a SqlCommand that tries to commit or rollback. If you're using TransactionScope, check the Complete() call—it's often missing or delayed.
  2. Rollback explicitly
    Inside your catch block, call transaction.Rollback() on the SQL transaction, or dispose the TransactionScope without calling Complete(). Never let the transaction linger. For example:
    using (var scope = new TransactionScope())
    {
    using (var conn = new SqlConnection(connectionString))
    {
    conn.Open();
    // ... your SQL commands
    scope.Complete();
    }
    }
  3. Shorten the transaction lifetime
    Move any long-running logic (API calls, file I/O, user input) outside the transaction. The transaction should only wrap the SQL operations. If you need to batch inserts, use table-valued parameters or bulk copy, not a single long transaction.
  4. Adjust timeout settings
    In your connection string, set Transaction Binding=Explicit Unbind if you're using SQL Server 2012 or later. This tells SQL Server to not implicitly promote the transaction to a distributed one—which often triggers handle expiration. Also, increase the TransactionScope timeout if you absolutely need it:
    var options = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(120) };
    using (var scope = new TransactionScope(TransactionScopeOption.Required, options))
    But only do this after you've confirmed the real fix (short transactions) doesn't work.
  5. Disable implicit transactions
    If your app uses ADO.NET with SqlConnection but doesn't explicitly start a transaction, check if your connection string has Implicit Transactions=True. This turns every statement into a transaction, and if the connection is idle too long, handles expire. Set it to False.

Alternative fixes if the main one fails

  • Upgrade SQL Server and Windows
    On Windows Server 2016 and earlier, the handle expiry timeout is shorter. Windows Server 2022 with SQL Server 2022 fixed several handle-related bugs (KB5021126). If you're on older builds, patching may help.
  • Use a connection pool with shorter lifetime
    Set Connection Lifetime=120 in your connection string to force reconnection before handles expire. This is a band-aid, not a fix, but it works.
  • Switch to System.Transactions with durable resource managers
    If you're using a custom resource manager (like a file system wrapper), replace it with SQL Server's built-in transaction support. The handle expiration is almost always caused by mixing file handles and SQL transactions.
  • Disable NTFS handle expiration (not recommended)
    You can tweak the kernel parameter HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Executive\AdditionalCriticalWorkerThreads but this is a global change that can destabilize your system. Don't do it unless you're desperate.

Prevention tip

The single most effective prevention is writing transactions that live less than 5 seconds. If you need to handle data across multiple operations, use a queue or a staging table instead of holding a transaction open. I've seen this error vanish completely after teams refactored their code to use shorter, more frequent commits. Also, monitor the SQL Server:Transactions performance counter—if you see transactions lasting more than 10 seconds, investigate.

Was this solution helpful?