Microsoft.Data.SqlClient.SqlException
A SqlException is thrown when SQL Server, or the driver in front of it, reports an error. It covers everything from a server that cannot be reached, to a query that has a syntax error, to a deadlock.
The message has the text, but the Number property is the one to use in code. It is the error number of SQL Server, such as 18456 for a failed login and 1205 for a deadlock, and the same number always has the same meaning.
Minimum version: >= 4.6.2 >= Core 2.1
Statistics
Common causes
The error number sorts the problems. These are the ones that you meet most often.
The server cannot be reached
The message says A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server name is wrong, the server is down, a firewall blocks port 1433, or the instance name is not resolved.
using var connection = new SqlConnection("Server=localhost,1;Database=Shop;Integrated Security=true;Connect Timeout=2;Encrypt=false");
connection.Open(); // SqlException
Check the server name, the port and the firewall from the machine that runs the code. Handle the failure where the database can be down:
using var connection = new SqlConnection("Server=localhost,1;Database=Shop;Integrated Security=true;Connect Timeout=2;Encrypt=false");
try
{
connection.Open();
}
catch (SqlException e) when (e.Number is -2 or 53 or 258)
{
logger.LogWarning(e, "The database is not available");
}
The login fails
Error 18456 means that the login was rejected. The user name or password is wrong, the user has no access to the database, or the server only accepts Windows accounts. The message has a state number that tells which of them it is.
Check the user and the password in the connection string, and that the user exists in the database. When the code runs in IIS or a service, the account is the one of the application pool or the service, not yours:
Server=tcp:myserver.database.windows.net,1433;Database=Shop;User ID=shop_app;Password=<secret>;Encrypt=True;
A query that takes too long
Error -2 is a timeout. The command ran for longer than CommandTimeout, which is 30 seconds by default. A missing index, a lock held by another query, or too much data are the usual reasons.
Look at the query first. Add the index or reduce the data. Raise the timeout only for commands that are meant to be slow, such as a report:
public async Task<int> CountAsync(SqlConnection connection)
{
using var command = new SqlCommand("SELECT COUNT(*) FROM Orders", connection);
command.CommandTimeout = 120;
return (int)await command.ExecuteScalarAsync();
}
A deadlock
Error 1205 means that two transactions waited for each other, and SQL Server ended one of them. It is safe to run the transaction again.
Retry a few times, and shorten the transactions and access the tables in the same order everywhere to reduce them:
public async Task SaveAsync(SqlConnection connection)
{
for (var attempt = 1; attempt <= 3; attempt++)
{
try
{
using var command = new SqlCommand("UPDATE Orders SET Status = 1", connection);
await command.ExecuteNonQueryAsync();
break;
}
catch (SqlException e) when (e.Number == 1205 && attempt < 3)
{
await Task.Delay(100 * attempt);
}
}
}
The data or the schema does not fit
Errors 2601 and 2627 are duplicate keys, 547 is a foreign key or check constraint, 208 is an invalid table name and 207 an invalid column name. The last two mean that the code and the database are on different versions, often after a deployment where the migration did not run.
Show the user a message for the constraint errors, and treat 207 and 208 as a deployment problem that has to be fixed in the database.
How to fix it and prevent it
Switch on the error number
Use e.Number, not the message text, which depends on the language of the server. A switch with the numbers that you handle keeps the code readable.
try
{
using var connection = new SqlConnection("Server=localhost,1;Database=Shop;Integrated Security=true;Connect Timeout=2;Encrypt=false");
connection.Open();
}
catch (SqlException e)
{
var message = e.Number switch
{
2601 or 2627 => "That value already exists.",
547 => "The item is still in use.",
1205 => "The database was busy, try again.",
_ => "Database error " + e.Number
};
Console.WriteLine(message);
}
Use the retry options of the driver or EF Core
Connections to Azure SQL fail for a short time now and then. Turn on the retry logic of the driver in the connection string, or EnableRetryOnFailure in Entity Framework, so these failures do not reach your code.
Server=tcp:myserver.database.windows.net,1433;Database=Shop;ConnectRetryCount=3;ConnectRetryInterval=10;
Dispose connections and use pooling
Open a connection late, and dispose it as soon as you can with using. The pool reuses the physical connections. If connections are not returned, the pool runs out, and the error is an InvalidOperationException that says Timeout expired, not a SqlException.
How to read the stack trace
The first line has the error number and the message. The frames in Microsoft.Data.SqlClient are the driver, and the first line in your code is the call that opened the connection or ran the command:
Microsoft.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - The wait operation timed out.)
---> System.ComponentModel.Win32Exception (258): The wait operation timed out.
at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command, Boolean callerHasConnectionLock, Boolean asyncClose)
at Microsoft.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, TimeoutTimer timeout, SqlConnectionString connectionOptions, Boolean withFailover)
at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
at Microsoft.Data.SqlClient.SqlConnection.Open()
at Shop.Services.CustomerRepository.Count() in C:\src\Shop\Services\CustomerRepository.cs:line 17
at Program.<Main>$(String[] args) in C:\src\Shop\Program.cs:line 4
ClientConnectionId:6f0c1f4e-3b8a-4c53-9d0e-5a9d2c1b7e42
Error Number:258,State:0,Class:20
The error number is 0x80131904, the general code, and the message says that the server could not be reached. The inner Win32Exception (258) is a timeout of the connection attempt. The call is connection.Open() on line 17 of CustomerRepository.cs, so look at the server name, the network and the firewall, and not at the query. The last lines have the Error Number as the driver sees it, and the ClientConnectionId that you can give to Azure support.
Should you catch it?
Yes. A database is a remote system, and it can fail in ways that you cannot check first. Catch SqlException around the call, filter on Number, and handle the errors that you know. Retry the transient ones, and let the others pass.
Do not show the message to users. It can contain the server name and other details that should stay on the server.
Find SqlException before your users do
elmah.io logs every unhandled exception in your .NET application with its stack trace and request details, groups identical errors and notifies you when a new one appears.
Start free trialRelated exceptions
- TimeoutException is thrown when a time limit set in code is reached.
- SocketException is thrown for errors on the network connection.
- InvalidOperationException is thrown when the connection pool is out of connections.
Frequently asked questions
What does error 258 mean?
The connection attempt timed out. The server did not answer, so check the name, the port and the firewall. It is the same as a timeout in the network library.
Why does my login fail when the password is correct?
The state number in the message tells the reason. It can be a user that does not exist in the database, a disabled login, or an application that runs under another account than you tested with.
Is it safe to retry after a deadlock?
Yes, if the whole transaction is run again. SQL Server has rolled it back. Do not retry a single statement in the middle of a transaction.
What is the difference between System.Data.SqlClient and Microsoft.Data.SqlClient?
The second is the current driver. The first is the old one that is only fixed for security problems. They have separate SqlException types, so a catch for one does not catch the other.
Let your AI agent track it down
Connect Claude Code, Cursor, VS Code or Visual Studio to the elmah.io MCP server and ask your agent to look into SqlException for you. For example:
The agent reads the stack trace and request details from elmah.io, finds the code in your repository and proposes a fix. In Claude Code, add the server with one command:
claude mcp add --transport http --client-id claudecode elmahio https://mcp.elmah.io/mcp
The MCP server is included on every plan and is currently in beta. Set up the MCP server.
Further reading
YouTube videos
Answers from Stack Overflow
This is due to breaking change in the query translation for EF Core 8 - see Contains in LINQ queries may stop working on older SQL Server versions.
Check the compatibility level of the database:
SELECT compatibility_level
FROM sys.databases
WHERE name = 'mydbname';
I see some databases are 110 and newer ones we created are 150
Pass the values to the context options (when registering in DI with AddDbContext for example):
opts
.UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(110)) // or 150
See the Mitigations part of the breaking change doc.
Also I would argue that you should consider upgrading the database (and server if needed) to support new features (see ALTER DATABASE SET COMPATIBILITY_LEVEL).
By Guru Stron. Read the original answer on Stack Overflow.
Microsoft.Data.SqlClient 4.0 is using ENCRYPT=True by default. Either you put a certificate on the server (not a self signed one) or you put
TrustServerCertificate=Yes;
on the connection string.
By Pieter van Kampen. Read the original answer on Stack Overflow.
If you're connecting to something unimportant, I was experiencing this error connecting from my dotnetcore API, to a microsoft sql server contained in a docker container (all locally) for dev work. My solution was putting encrypt=False at the end of my connection string like so (within appsettings.json):
"DefaultConnection": "server=localhost;database=newcomparer;trusted_connection=false;User Id=sa;Password=reallyStrongPwd123;Persist Security Info=False;Encrypt=False"
By Eric Milliot-Martinez. Read the original answer on Stack Overflow.
The two classes are different but they do inherit from the same base class, DbException. That's the common class for all database exceptions though and won't have all the properties in the two derived classes
You should inspect the libraries/NuGet packages you use and ensure you use versions that support the new Microsoft.Data.SqlClient library. Mixing up data providers isn't fun and should be avoided when possible. Most popular NuGet packages use Microsoft.Data.SqlClient already.
If you can't do that, the options depend on how you actually handle database exceptions. Do you inspect the SQL Server-specific properties or not?
Another option is to postpone upgrading until all NuGet packages have upgraded too. Both libraries include native DLLs that need to be included during deployment. If you mix libraries, you'll have to include all native DLLs.
This can be painful.
Handling the exceptions
If both libraries need to be used, each exception type needs to be handled separately. Pattern matching makes this a bit easier :
switch (ex)
{
case System.Data.SqlClient.SqlException exc:
HandleOldException(exc);
return true;
case Microsoft.Data.SqlClient.SqlException exc:
HandleNewException(exc);
return true;
case DbException exc:
HandleDbException(exc);
return true;
default:
return false;
}
Mapping Exceptions
Another option could be to map the two exception types to a new custom type that contains the interesting properties. You'd have to map both the SqlException and SqlError classes. Using AutoMapper would make this easier:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<System.Data.SqlClient.SqlException, MySqlException>();
cfg.CreateMap<System.Data.SqlClient.SqlError, MySqlError>();
cfg.CreateMap<Microsoft.Data.SqlClient.SqlException, MySqlException>();
cfg.CreateMap<Microsoft.Data.SqlClient.SqlError, MySqlError>();
});
This would allow mapping either exception to the common MySqlException type :
var commonExp=mapper.Map<MySqlException>(ex);
By Panagiotis Kanavos. Read the original answer on Stack Overflow.
I hit this issue as well. Net 6.0
It suddenly started occurring when I upgraded Microsoft.EntityFrameworkCore.SqlServer
FROM Version="6.0.0" to Version="6.0.1"
I added Encrypt and TrustServerCertificate to connection string.
data source=(local);Initial Catalog=XXXXXXX;Trusted_Connection=True;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=True
By Steve G.. Read the original answer on Stack Overflow.
Source: Stack Overflow