How to Handle T-SQL Errors Properly [Extensive guide]
Introduction

As a Backend Software Engineer, you will be working with databases a lot on a regular basis, so you should get comfortable reading SQL errors to properly fix them, so it's an essential skill for you as a developer and troubleshooter as well.
Errors in T-SQL are of many types, based on the error you get, you can decide the optimal solution for it. So In this article, I will show you how to read, handle and even customize your errors.
Table of Contents
Starting with error handling.
Raising, throwing, and customizing your errors.
Resources.
Error Handling
let's begin with an example to demonstrate the concept of errors on SQL server Imagine you have a database consisting of Products, Buyers, Staff, and Orders and you have a unique constraint on the product table (on product_name)
And you tried to insert a product with a name that already exits
🤔 what do you think you will get? Of course an error
we can handle this kind of Error using the Try catch block.
The syntax of try-catch on SQL is nearly similar to the ones on programming languages, you begin with try and end with catch, you see it's that simple 😄.
The General syntax
In case of your query inside Try throw an error, then you place your error handling statements with the catch block. If there is no error, the catch block is skipped
An example of a statement that fails
An error occurred! You are in the CATCH block
An example of successful Insertion
Product inserted correctly!
Nested try..catch
A try block or a catch block can nest another try-catch block.
an example of nested try-catch
An error occurred inserting the product! You are in the first CATCH block
An error occurred inserting the error! You are in the second CATCH block
Exercise
You have a stock attribute on products table and you can't have negative values on this attribute.
An error occurred!
Error Anatomy and uncatchable error
📓note:
Not all errors are catchable
Let's break down the error message, to compose a useful information and know exactly how to handle error based on it's Anatomy
The first line is
error number-> sql errors from1 to 49999
You can also create your own starting from 50001
select * from sys.message -> to know the complete log of error numbers
2627
1033
14
..
Violation of %ls constraint '%.*ls'. Cannot insert duplcate...
The second value is
severity levelfrom
0 - 10: informational messages (warnings)from
11 - 16: errors that can be corrected by the user (constraint violation, etc.)from
17 - 26: other errors (software problems, fatal errors)
you can see the whole list through the docuemntation
the third value is the state: it gives you more information about the error 1: if SQL server display an error 0-255: own errors -> to raise your own error
The fourth value is
Line-> give you the line number.
Finally, if the error happens within a stored procedureor atrigger`, you will receive extra data giving you the name of the stored procedure or the name of the trigger
Uncatchable Errors
the try cath we've learned can't catch every kind of error.
Severity lower than 11 (Uncatchable)
Severity 11 -> 19 (catchable)
Compilation errors: objects and columns that don't exist
the severity of 20 or higher that stopped the connection will not be caught but if it didn't cut the connection, it will be caught
compilation error: object and column that doesn't exist
An example
notice the output?
It doesn't give you the actual error which is you are in the CATCH Block As this is a compilation error, the CATCH block can t handle the error
Giving information about the error
and this is the error returned from the catch block
An error occurred! You are in the CATCH block
sometimes the default error the query throws is very useful, and by overriding it using CATCH with an error statement we lose the default, however, can still retrieve it using
Error functions
ERROR_NUMBER() returns the number of the error. ERROR_SEVERITY() returns te error severity (11-19) ERROR_STATE() returns the state of the error ERROR_LINE() returns the number of the line error ERROR_PROCEDURE() returns the name of the stored proc/trigger, Null if there is no stored pro/trig Error_message()
An Example
2627
14
1
NULL
2
Violation of UNIQUE KEY constraint 'unique_name'...
⛔Warning:
We can't use error functions outside the catch block
you will get nulls
we can use it inside nested try and catch, But in this case, you will get the last error that occurred
Raising, throwing and customizing your errors
In this section, we will learn
How to raise errors.
Re-throw original errors.
Create your own defined errors.
Raise errors statements
SQL Server provides two statements to raise errors
RAISEERRORTHROW(Microsoft recommend using it on new application)
RAISERROR syntax
the first parameter can be a message string, a message-id, or a variable that contains the message string.
the second Parameter -> severity
the third -> state
you can optionally add arguments, like strings or numbers
if the message string has some parameter placeholders such as %s or %d, these arguments will replace them
RAISERROR with message string
If we don't specify an error number, the error number will always be 50000
Let's change the message text with placeholders
It's recommended to look at Microsoft documentation for more information
RAISERROR with error number
This error number comes from sys.messages
RIASERROR - Example with Try .. Catch
As you might've guessed, errors below 11 are not catchable.
if we changed the severity level from 9 to 11, the error will be caught by the catch
You are in the CATCH Block
Exercise
Throw statment
Recommended by Microsoft over RAISERROR statement
General syntax
📓note: unlike the RAISERROR statement, the THROW statement allows re-throwing an original error caught by a CATCH block
Throw - Without parameters
The original error caused from the try block is dividing by 0, so the output mesasge is the thrown original error
and SELECT statement inside catch has not been executed;
be careful when writing THROW at the end, you should put a semi-colon before the line
This line is executed!
SQL Server thinks that the word THROW is an alias for the select statement
Throw - with parameters
This syntax can be included within a CATCH block or outside of it.
An example
But only statements with no parameter should be put on the catch block
This is an example
Another example
customizing error messages in THROW statements
⚠️Warning:
throw statement doesn't allow the inclusion of parameters placeholders such as %d or %s but we have a hack around this by
Variable by concatenating strings
FORMATEMESSAGE()function
Using a variable and the CONCAT function
Using FORMATEMESSAGE function
we can include wild cards
notice that the throw statement doesn't allow the specification of the severity, SQL server always sets it to 16
FORMATEMESSAGE() with message number
In SQL server we have a view sys.messages which contains messages with according message_id
You'll get a view
101
1033
15
0
Query not allowed in Waitfor
...
...
...
...
...
we can choose any message_id or add a new message to this view to customize our errors.
To add a new message to sys.messages, we can execute the sp_addmessage stored procedure with the following parameters
msg_id must be greater than 500000
language is optional, if you don't specify it, it would be the default language of the session.
we can now use this new message_id on th FORMATEMESSAGE()
notice that the throw statement doesn't allow the specification of the severity, SQL server always sets it to 16
A Detailed Example
Resources
Last updated