- Error number
- 134
- Severity
- 15 (user-correctable error)
- State
- 1
- Product
- Microsoft SQL Server Database Engine
- Versions
- All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
- Category
- Syntax & Object Resolution
- Last reviewed
- 24 October 2026
On this page
What it means
Error 134 means the same @variable name is declared twice in one batch or one procedure, which T-SQL does not allow because a variable lives for the whole batch, not for the block it is declared in.
In T-SQL the scope of a local variable is the whole batch (or the whole body of a procedure, function or trigger) from the DECLARE onwards. BEGIN ... END, IF, ELSE and WHILE do not open a new scope, unlike in C# or Java. So two DECLAREs of the same name anywhere in one batch collide, even when one is inside a loop or a branch that would never run. The parser rejects the batch with 134 at severity 15 before any statement executes, and names the second declaration.
Procedure parameters count as declarations too, so DECLARE @rate in a procedure that already has a @rate parameter fails the same way. Splitting a script with GO ends the batch and its variables, which is why a duplicate name is fine across batches, and why the opposite mistake, using a variable after GO, raises error 137.
Common causes
- Two script snippets pasted into one batch, each with its own
DECLARE @total. - A DECLARE inside a WHILE loop or IF block for a name already declared earlier in the batch, on the assumption that blocks have their own scope.
- The same variable declared in both the IF and the ELSE branch.
- A procedure body that declares a variable with the same name as one of its parameters.
- Names that differ only in case, such as
@Totaland@TOTAL, on an instance with a case-insensitive collation.
Diagnose it
Read-only These queries only read metadata and dynamic management views. They do not change data, permissions or configuration, so they are safe to run first.
Check a corrected batch compiles without running it
sp_describe_first_result_set parses and binds the batch it is given and reports 134 if a name is still declared twice, without executing anything.
EXEC sp_describe_first_result_set
N'DECLARE @total int = 0;
SET @total = @total + 5;
SELECT @total AS total;'; One row describing the total column. Error 134 instead means a duplicate DECLARE remains.
List the parameters an existing procedure already declares
When an ALTER PROCEDURE fails with 134, compare the names you DECLARE in the body with the parameter names.
SELECT p.parameter_id, p.name AS parameter_name, TYPE_NAME(p.user_type_id) AS data_type
FROM sys.parameters AS p
WHERE p.object_id = OBJECT_ID(N'dbo.ApplyDiscount')
ORDER BY p.parameter_id; Every name listed here is already declared inside the procedure body; a DECLARE with the same name raises 134.
How to fix it
Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.
Declare once at the top, assign with SET later
Read-onlyKeep one DECLARE per name, at the start of the batch, and use SET or SELECT to give it new values. Inside a loop, reset it explicitly at the start of each pass.
DECLARE @i int = 1, @msg varchar(20);
WHILE @i <= 3
BEGIN
SET @msg = 'row ' + CAST(@i AS varchar(5)); -- assign, do not redeclare
PRINT @msg;
SET @i += 1;
END;
Give the second variable its own name
Read-onlyWhen the two declarations really hold different things (an order total and an invoice total), name them after what they hold.
DECLARE @order_total decimal(10,2) = 0;
DECLARE @invoice_total decimal(10,2) = 0;
Split independent snippets with GO
Client or network sideIf the snippets are independent, end each batch with GO in SSMS or sqlcmd. Each batch then gets its own variables. Do not do this when a later part still needs the earlier values.
DECLARE @total int = 0;
SELECT @total AS first_total;
GO
DECLARE @total int = 5;
SELECT @total AS second_total;
In a procedure, use the parameter instead of redeclaring it
Changes schemaAssign a default to the parameter with SET or ISNULL instead of declaring a variable with the same name.
CREATE OR ALTER PROCEDURE dbo.ApplyDiscount
@order_id int,
@rate decimal(4,2) = NULL
AS
BEGIN
SET @rate = ISNULL(@rate, 0.10);
SELECT @order_id AS order_id, @rate AS rate;
END;
Examples
Two snippets pasted into one batch
DECLARE @total int = 0;
SELECT @total AS order_count;
DECLARE @total int = 5; -- second snippet, same name
SELECT @total AS invoice_count;
Msg 134, Level 15, State 1, Line 4 The variable name '@total' has already been declared. Variable names must be unique within a query batch or stored procedure.
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
A DECLARE inside a loop for a name declared earlier
DECLARE @i int = 1;
DECLARE @msg varchar(20);
WHILE @i <= 3
BEGIN
DECLARE @msg varchar(20) = 'row'; -- a loop is not a new scope
SET @i += 1;
END;
Msg 134, Level 15, State 1, Line 5 The variable name '@msg' has already been declared. Variable names must be unique within a query batch or stored procedure.
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
A procedure that redeclares its own parameter
CREATE PROCEDURE dbo.ApplyDiscount @order_id int, @rate decimal(4,2)
AS
DECLARE @rate decimal(4,2) = 0.10; -- @rate is already a parameter
SELECT @order_id AS order_id, @rate AS rate;
Msg 134, Level 15, State 1, Procedure ApplyDiscount, Line 3 The variable name '@rate' has already been declared. Variable names must be unique within a query batch or stored procedure.
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
How to prevent it
Declare all variables of a batch or procedure in one block at the top, with names that say what they hold. That makes duplicates obvious in review and avoids the loop trap: a DECLARE without a value inside a WHILE loop does not reset the variable on the next pass, so the old value leaks into the next iteration.
When you assemble scripts from several files, keep each file as its own batch ending in GO. For more on how batches, procedures and variables fit together, see stored procedures and functions.
Common questions
Can I declare a variable inside a WHILE loop?
Yes, once per batch. The DECLARE is processed at compile time, so the variable exists for the whole batch, and a DECLARE without a value does not reset it on each pass. Set it explicitly at the start of the loop body.
Why does declaring the same variable in IF and in ELSE fail?
IF and ELSE do not create scopes in T-SQL. Both DECLAREs are in the same batch, so the second one raises 134 even though only one branch can run.
What is the difference between error 134 and error 137?
Error 134 is a name declared twice in one batch. Error 137 is a name used without being declared in the current batch, often because a GO ended the batch that declared it.
Learn more on CodeWithSQL
Official documentation
- DECLARE @local_variable (Transact-SQL)
- Variables (Transact-SQL)
- Database Engine events and errors 0 to 999
Last technically reviewed 24 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.