SQL Server Error Severities Explained

🚨Part of the SQL Server Errors series, the exact messages and what actually causes them.
⚡Read the Level before you read the message. 0 to 10 is informational, 11 to 16 means the statement is wrong and the server is fine, 17 to 19 is a resource limit or an engine fault and it is yours to investigate, and 20 and above has ended the task and usually the connection, so the truth is in the error log.

You have an error in front of you and need to know how worried to be. The Level in Msg 229, Level 14, State 5 is the severity, and it tells you before you read the message whether the statement was wrong, the server ran short of something, or the engine itself is in trouble. The bands follow MS Docs on Database Engine error severities.


SQL Server Error Severity Levels

SeverityMeaningErrors you will meet
0 to 9Informational messages. The engine raises none of its own errors at 0 to 9.
10Informational message (returned as severity 0 to the client).825 read retry, still a disk warning
11Object or entity does not exist.
12Query using non-locking hints; potential inconsistent reads.
13Deadlock detected.1205 deadlock victim
14Security-related error (permission denied).229 permission denied
18456 login failed
15Syntax error in T-SQL.102 incorrect syntax
16General user-correctable error.208 invalid object name
8134 divide by zero
17Out of resources (memory, locks, disk space) or over a limit the administrator set.1105 filegroup full
9002 log full
18Engine software error. The statement completes and the connection stays open.
19Nonconfigurable engine limit exceeded. The batch is stopped.701 insufficient system memory
20Fatal error affecting the current task.
21Fatal error affecting all tasks in the current database.3417 cannot recover master
22Table or index corruption.
23Database integrity in question.
24Media failure or hardware-related issue.823 and 824 I/O errors

Operational notes:

  • Severity 19 to 24 is always written to the SQL Server error log. Lower ones get there only when the message is flagged as event-logged or raised WITH LOG, which is why 18456 at Level 14 and 825 at Level 10 show up in it.
  • Engine errors at 19 and above stop the current batch.
  • Severity 20 and above ends the task, and in most cases the connection with it.
  • TRY…CATCH cannot handle an error that ends the connection.

How to Read an Error in SSMS

Every error carries a number, a message, a Level (the severity), a State and a Line. This one is quick to reproduce.

Run it as a database user with no permission on the table:

-- DemoSeverityUser has no rights on this table, so the read is refused
EXECUTE AS USER = 'DemoSeverityUser';
GO
SELECT * FROM dbo.SeverityDemo;
GO
REVERT;
GO
SSMS Messages paneexample output, not something to copy
Msg 229, Level 14, State 5, Line 4 The SELECT permission was denied on the object 'SeverityDemo', database 'DemoDatabase', schema 'dbo'.

Level 14 says security before you read the message: the engine is fine and the fix is a permission. The State tells the engine’s own call sites apart, and the Line points at the statement. The number at the start is how the SQL Server Errors library is indexed.


Severity Ranges in Production Terms

Instead of memorising every definition, think of severity in ownership terms:

0 to 10
Informational

Not a failure, with one exception: error 825 is Level 10 and means a read only worked on a retry.Ignore the band, never that number.

11 to 16
Application or query issue

Bad T-SQL, missing objects, permissions, constraint violations, deadlocks.Usually owned by development or query logic.

17 to 19
Capacity or engine pressure

Memory exhaustion, lock limits, a full log or filegroup, and engine software faults at 18.Owned by the DBA. Investigate configuration and workload.

20 to 24
Stability or data risk

Connection termination, corruption, database integrity issues, storage failure.Immediate investigation required. May involve CHECKDB, restore strategy, or storage review.

Amber goes back to whoever wrote the statement. Orange and red come to you.


TRY…CATCH and ERROR_SEVERITY()

TRY...CATCH catches runtime errors above severity 10 that do not end the connection. A severity 10 message raised inside the TRY block goes straight to the client and execution carries on.

If the error ends the connection (usually severity 20 and above), execution stops and control never reaches the CATCH block.

Inside a CATCH block, you can retrieve the severity of the error using ERROR_SEVERITY():

-- Generates severity 16
BEGIN TRY
    SELECT 1 / 0;  -- Generates severity 16
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER()    AS ErrorNumber,
        ERROR_SEVERITY()  AS ErrorSeverity,
        ERROR_STATE()     AS ErrorState,
        ERROR_MESSAGE()   AS ErrorMessage;
END CATCH;
SQL Server SSMS TRY CATCH output showing ERROR_NUMBER 8134 and ERROR_SEVERITY 16

Error 8134 at severity 16: user-correctable, the connection stays open and execution lands in the CATCH block.

ERROR_SEVERITY() returns the severity of the error that triggered that specific CATCH block. If it is called outside the scope of a CATCH block, it returns NULL.

MS Docs covers ERROR_SEVERITY() inside nested TRY…CATCH blocks.


Where Errors Are Stored

All system and user-defined error messages are stored in the sys.messages catalog view.

It holds the definition of each error, not a history of when one happened. This counts the engine messages at each severity:

-- Engine messages per severity, and how many are always logged when raised
SELECT
    severity,
    COUNT(*)                          AS messages,
    SUM(CAST(is_event_logged AS int)) AS event_logged
FROM sys.messages
WHERE language_id = 1033
GROUP BY severity
ORDER BY severity;
sys.messages on the lab instanceSQL Server 2025 CU8, example output, not something to copy
severity messages event_logged -------- -------- ------------ 0 104 0 10 2728 443 11 84 0 12 1 0 13 4 1 14 96 17 15 571 1 16 12578 508 17 268 37 18 52 4 19 5 4 20 136 86 21 104 68 22 20 5 23 20 9 24 10 8

3 in 4 engine messages are 16 (12,578 of 16,781). Nothing is defined above 24: severity 25 exists only for RAISERROR.

To find when an error happened, look in:

  • The SQL Server error log
  • Windows Event Viewer
  • Extended Events
  • Custom logging tables

Reading the log itself. Severity tells you how bad one message is; the log tells you whether it is happening repeatedly, and at what time of day.


Application Errors: RAISERROR and THROW

An application error carries whatever severity the code chose, and it never reaches the error log unless the code asks. From a login with no server roles:

RAISERROR('severity 10 from a plain login', 10, 1);
RAISERROR('severity 11 from a plain login', 11, 1);
RAISERROR('severity 16 from a plain login', 16, 1);
RAISERROR('severity 18 from a plain login', 18, 1);
RAISERROR('severity 16 WITH LOG', 16, 1) WITH LOG;
sqlcmd on the lab instanceexample output, not something to copy
severity 10 from a plain login Msg 50000, Level 11, State 1, Server HPAI01, Line 2 severity 11 from a plain login Msg 50000, Level 16, State 1, Server HPAI01, Line 3 severity 16 from a plain login Msg 50000, Level 18, State 1, Server HPAI01, Line 4 severity 18 from a plain login Msg 2778, Level 16, State 2, Server HPAI01, Line 5 Only System Administrator can specify WITH LOG option for RAISERROR command.

Severity 10 comes back as a plain message with no Msg line, because the client receives it as severity 0. 11 to 18 come back as errors and the batch carries on. None of it reached the error log.

Who can do whatAny login: severity 0 to 18, returned to the client only.
WITH LOG: sysadmin or ALTER TRACE, anyone else gets Msg 2778. Writes to the error log and the Windows Application log.
Severity 19 to 25: sysadmin or ALTER TRACE, and only with WITH LOG, otherwise Msg 2754.
THROW: always severity 16, unless it rethrows a caught error, which keeps its own severity.

The rules are in MS Docs for RAISERROR and THROW. To keep application errors, log ERROR_NUMBER(), ERROR_SEVERITY(), ERROR_MESSAGE() and SYSDATETIME() to a table from the CATCH block.



Frequently Asked Questions

Which severity levels should actually wake someone up?
Severity 17 and above. Below that the statement failed but the server is fine, and it belongs in a morning report rather than a phone call. 19 to 24 also land in the error log on their own, which makes them the easiest to alert on. Add 825 (Level 10) and bursts of 18456 (Level 14), which hide below the line.
Why do some errors never reach my application?
Severity 20 and above usually end the connection, so the client often sees a generic transport failure rather than the real message. The SQL Server error log has the truth, which is why almost every post in this series starts by reading it.
Can TRY…CATCH handle every error?
No. TRY…CATCH does not catch an error that ends the connection (usually severity 20 and above), or compile errors in the same batch. Severity 10 and below are messages, so they never enter the CATCH block either. It is for statement-level problems, not for anything that kills the session.
The error number is not in any of these posts. What now?
Look up the severity first, because that tells you whether it is a statement problem or a server problem, and the two need completely different responses. sys.messages holds every message text and severity the instance knows about.
Why is my application error not in the SQL Server error log?
RAISERROR only writes there with WITH LOG, which needs sysadmin or ALTER TRACE, and THROW cannot log at all. Log application errors to your own table from the CATCH block.

Summary

Severity is the quickest triage signal SQL Server gives you.

  • 16 usually means fix the code.
  • 17 means check resources.
  • 22 or 23 means check integrity.
  • 24 means check storage.

Reading severity first tells you who likely owns the issue and how worried you should be before you even dig into the full message text.


Where To Go Next

🔍 Have an actual error in front of you? Severity tells you how worried to be. The guide tells you what the message means and what causes it. Find your error →

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *