Understanding the Error

The error “Communication with the underlying transaction manager has failed” occurs when the Microsoft Distributed Transaction Coordinator (MSDTC) service on one machine cannot communicate with the MSDTC service on another machine to coordinate a distributed transaction.

The full error and inner exception typically look like this:

System.Transactions.TransactionManagerCommunicationException:
Communication with the underlying transaction manager has failed.

Inner Exception:
The MSDTC transaction manager was unable to pull the transaction from the
source transaction manager due to communication problems. Possible causes are:
a firewall is present and it doesn't have an exception for the MSDTC process,
the two machines cannot find each other by their NetBIOS names, or the support
for network transactions is not enabled for one of the two transaction managers.
(Exception from HRESULT: 0x8004D02B)

This commonly occurs when:

  • A .NET application uses TransactionScope across multiple SQL Server instances
  • A stored procedure uses linked servers with BEGIN DISTRIBUTED TRANSACTION
  • WCF services use transactional bindings across machines

When Distributed Transactions Are Triggered

You may not realize your application uses distributed transactions. They are automatically escalated in these scenarios:

// This creates a distributed transaction if conn1 and conn2 point to different servers
using (TransactionScope scope = new TransactionScope())
{
    using (SqlConnection conn1 = new SqlConnection("Server=Server1;Database=DB1;..."))
    using (SqlConnection conn2 = new SqlConnection("Server=Server2;Database=DB2;..."))
    {
        conn1.Open();
        // Execute work on Server1

        conn2.Open();  // This escalates to a distributed transaction
        // Execute work on Server2
    }
    scope.Complete();
}

Even opening two connections to the same server can escalate to MSDTC in some cases (prior to .NET 4.0 with SQL Server 2008).

Step-by-Step Fix

Step 1: Configure MSDTC Security Settings

This must be done on both machines involved in the transaction.

  1. Open Component Services by running dcomcnfg
  2. Navigate to: Component Services > Computers > My Computer > Distributed Transaction Coordinator > Local DTC
  3. Right-click Local DTC and select Properties
  4. Go to the Security tab
  5. Configure the following settings:
SettingValue
Network DTC AccessChecked
Allow Remote ClientsChecked
Allow InboundChecked
Allow OutboundChecked
Mutual Authentication RequiredSee note below
No Authentication RequiredSelect this if machines are NOT in the same domain
Enable XA TransactionsChecked (if using XA)
Enable SNA LU 6.2 TransactionsLeave unchecked unless needed

Authentication note: If both machines are in the same Active Directory domain, use Mutual Authentication Required. If they are in different domains or workgroups, use No Authentication Required.

  1. Click OK and confirm that you want to restart the MSDTC service

Step 2: Configure Windows Firewall

The inner exception specifically mentions the firewall. On Windows 7, Windows Server 2008, and later versions, you must explicitly enable the MSDTC firewall exception, even if you think you have already done so.

Using Windows Firewall with Advanced Security

# Enable the predefined MSDTC firewall rules (do this on BOTH machines)
Enable-NetFirewallRule -DisplayGroup "Distributed Transaction Coordinator"

Or manually through the GUI:

  1. Open Windows Firewall with Advanced Security (wf.msc)
  2. Click Inbound Rules
  3. Find the rules in the Distributed Transaction Coordinator group
  4. Enable all of them (right-click > Enable Rule):
    • Distributed Transaction Coordinator (RPC)
    • Distributed Transaction Coordinator (RPC-EPMAP)
    • Distributed Transaction Coordinator (TCP-In)

Repeat for Outbound Rules as well.

By default, MSDTC uses dynamic RPC ports, which makes firewall configuration difficult. You can assign a fixed port:

  1. Open Component Services (dcomcnfg)
  2. Navigate to My Computer > Distributed Transaction Coordinator > Local DTC > Properties
  3. There is no GUI for the port setting — use the registry:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC]
"ServerTcpPort"=dword:00001388

This sets MSDTC to use TCP port 5000 (0x1388). Then create a specific firewall rule:

New-NetFirewallRule -DisplayName "MSDTC Fixed Port" -Direction Inbound -Protocol TCP -LocalPort 5000 -Action Allow
New-NetFirewallRule -DisplayName "MSDTC RPC" -Direction Inbound -Protocol TCP -LocalPort 135 -Action Allow

After changing the port, restart the MSDTC service:

Restart-Service -Name MSDTC

Step 3: Verify NetBIOS Name Resolution

The two machines must be able to resolve each other by NetBIOS name, not just by IP address or FQDN. MSDTC uses the NetBIOS name internally.

Test from each machine:

:: From the client, test resolution of the server
ping SERVER1
nbtstat -a SERVER1

:: From the server, test resolution of the client
ping CLIENT1
nbtstat -a CLIENT1

If name resolution fails, add entries to the hosts file on both machines:

# C:\Windows\System32\drivers\etc\hosts
10.0.1.10    SERVER1
10.0.1.20    CLIENT1

Or better, ensure WINS or DNS with appropriate records is configured.

Step 4: Verify the MSDTC Service Is Running

# Check MSDTC service status on both machines
Get-Service -Name MSDTC | Select-Object Name, Status, StartType

# Ensure it is set to start automatically
Set-Service -Name MSDTC -StartupType Automatic
Start-Service -Name MSDTC

Step 5: Test MSDTC Communication

Use the DTCPing tool from Microsoft to test MSDTC connectivity between two machines.

  1. Download DTCPing from Microsoft
  2. Run dtcping.exe on both machines simultaneously
  3. On each machine, enter the other machine’s NetBIOS name
  4. Click Ping — a successful test shows “successfully completed” for both the RPC and transaction tests

Alternatively, test from PowerShell:

# Test RPC endpoint mapper connectivity
Test-NetConnection -ComputerName SERVER1 -Port 135

# Test the MSDTC fixed port (if configured)
Test-NetConnection -ComputerName SERVER1 -Port 5000

Distributed Transactions with SQL Server

Scenario: Linked Server Queries

When SQL Server executes a query across a linked server, MSDTC is involved:

-- This triggers a distributed transaction
BEGIN DISTRIBUTED TRANSACTION
    INSERT INTO LocalDB.dbo.Orders (OrderID, CustomerName)
    SELECT OrderID, CustomerName FROM [RemoteServer].RemoteDB.dbo.Orders
    WHERE OrderDate > '2024-01-01';
COMMIT TRANSACTION

Ensure MSDTC is configured on both SQL Server machines.

Scenario: .NET TransactionScope

using (var scope = new TransactionScope())
{
    using (var conn = new SqlConnection(connectionString1))
    {
        conn.Open();
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO Table1 (Col1) VALUES ('Value1')";
            cmd.ExecuteNonQuery();
        }
    }

    using (var conn = new SqlConnection(connectionString2))
    {
        conn.Open();  // Escalates to distributed transaction
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO Table2 (Col1) VALUES ('Value2')";
            cmd.ExecuteNonQuery();
        }
    }

    scope.Complete();
}

Avoiding Unnecessary Distributed Transactions

If both connections target the same SQL Server instance, you can avoid MSDTC escalation by reusing a single connection:

using (var scope = new TransactionScope())
using (var conn = new SqlConnection(connectionString))
{
    conn.Open();

    using (var cmd1 = conn.CreateCommand())
    {
        cmd1.CommandText = "INSERT INTO DB1.dbo.Table1 (Col1) VALUES ('Value1')";
        cmd1.ExecuteNonQuery();
    }

    using (var cmd2 = conn.CreateCommand())
    {
        cmd2.CommandText = "INSERT INTO DB2.dbo.Table2 (Col1) VALUES ('Value2')";
        cmd2.ExecuteNonQuery();
    }

    scope.Complete();
}

Troubleshooting Checklist

Run through this checklist on both machines:

  1. MSDTC service is running and set to Automatic start
  2. Component Services security settings are correct (Network DTC Access enabled, Allow Inbound/Outbound)
  3. Windows Firewall has the Distributed Transaction Coordinator exception enabled for both inbound and outbound
  4. NetBIOS name resolution works in both directions
  5. Same MSDTC authentication level on both machines (Mutual Authentication, Incoming Caller Authentication, or No Authentication)
  6. RPC port 135 is accessible between the machines
  7. DTCPing test succeeds in both directions
  8. Antivirus is not blocking MSDTC or RPC traffic
  9. SQL Server has remote access enabled (sp_configure 'remote access', 1)
  10. Clustered environments: If SQL Server is clustered, configure MSDTC as a clustered resource, not just on the individual nodes

Common Pitfalls

Windows Firewall “Looks” Open But Is Not

On Windows 7 and Server 2008+, the firewall may appear to have MSDTC exceptions enabled, but the rules may be applied to the wrong network profile (Domain vs Private vs Public). Verify:

Get-NetFirewallRule -DisplayGroup "Distributed Transaction Coordinator" |
    Select-Object Name, Enabled, Profile, Direction

Ensure the rules are enabled for the correct profile.

Different MSDTC Instances in a Cluster

On a Windows Server Failover Cluster, each clustered instance of SQL Server should have its own MSDTC resource. The local MSDTC on the node may not be the one handling transactions for the clustered SQL instance.

MSDTC Log File Corruption

If the MSDTC log file is corrupted, the service may start but fail to process transactions:

:: Reset the MSDTC log
msdtc -resetlog

Summary

The “Communication with the underlying transaction manager has failed” error is a firewall and MSDTC configuration issue. To fix it, open Component Services on both machines and enable Network DTC Access with Allow Inbound and Allow Outbound. Then enable the Distributed Transaction Coordinator firewall exception in Windows Firewall on both machines. Verify that the machines can resolve each other by NetBIOS name, and use DTCPing to validate end-to-end MSDTC communication.