- Error number
- 137
- Severity
- 15 (user-correctable error)
- State
- 2 (see the state table below)
- 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
- 23 October 2026
On this page
What it means
Error 137 means the batch uses a variable such as @id that has not been declared in that same batch or scope.
A T-SQL variable exists only from its DECLARE to the end of the batch (or the procedure, function or dynamic SQL statement) that declares it. When the parser meets @name and no declaration is visible in the current scope, it raises 137 at severity 15 and the batch does not run. The name in double quotes is the variable that could not be found.
Three scope boundaries cause most cases. GO is not T-SQL: SSMS and sqlcmd split the script at each GO and send the parts as separate batches, so a variable declared above a GO is gone below it. Dynamic SQL run with EXEC or sp_executesql is its own batch and cannot see the caller's variables unless they are passed as parameters. Inside a procedure, the parameter list is the declaration, so a body that uses a different spelling fails when the procedure is created. A table variable that is not declared raises the sibling error 1087 instead.
| State | Meaning |
|---|---|
| 1 | Observed when assigning to an undeclared variable with SET. |
| 2 | Observed when an undeclared variable is read in an expression (WHERE, SELECT list, procedure body, dynamic SQL). |
Common causes
- The variable is declared above a GO separator and used below it, often after running only part of a script or adding a GO between steps.
- Dynamic SQL (sp_executesql or EXEC) refers to a variable of the calling batch that was not passed as a parameter.
- A typo or different spelling between the DECLARE (or procedure parameter) and the place it is used.
- Running only the selected lines in SSMS, without the DECLARE statement above them.
- A query copied from application code where @id was a client-side parameter the driver would have supplied.
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.
Compile a parameterised statement with its parameter list
sp_describe_first_result_set accepts the same parameter declaration as sp_executesql. If the statement compiles with the list, the variable only needs to be passed in.
EXEC sp_describe_first_result_set
N'SELECT name FROM dbo.Customers WHERE id = @id;',
N'@id INT'; A row for the name column means every variable is declared by the parameter list; 137 names any variable still missing.
List the real parameter names of a procedure
When 137 is raised while creating or altering a procedure, compare the names used in the body with the declared parameters.
SELECT 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.usp_GetCustomer')
ORDER BY p.parameter_id; The exact parameter names (for example @CustomerId), so a mismatched spelling in the body stands out.
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 the variable in the same batch
Read-onlyRemove the GO between the DECLARE and the statement that uses it, or repeat the DECLARE after the GO. When running part of a script in SSMS, include the DECLARE in the selection.
DECLARE @id INT = 5;
SELECT name
FROM dbo.Customers
WHERE id = @id;
Pass variables into sp_executesql as parameters
Changes dataDeclare the parameters in the second argument and pass the values after it. The names inside the dynamic statement must match the declaration, not the caller's variables, although using the same names keeps it readable.
DECLARE @id INT = 5, @city NVARCHAR(50) = N'Leeds';
EXEC sp_executesql
N'UPDATE dbo.Customers SET city = @city WHERE id = @id;',
N'@id INT, @city NVARCHAR(50)',
@id = @id, @city = @city;
Make the procedure body match its parameter names
Changes schemaCorrect the spelling in the body (or the parameter list) and recreate the procedure. Callers that pass parameters by name must use the declared name.
CREATE OR ALTER PROCEDURE dbo.usp_GetCustomer
@CustomerId INT
AS
SELECT name FROM dbo.Customers WHERE id = @CustomerId;
Examples
A GO between the DECLARE and the query
CREATE TABLE dbo.Customers (id INT PRIMARY KEY, name NVARCHAR(100));
DECLARE @id INT = 5;
GO -- ends the batch, and @id with it
SELECT name FROM dbo.Customers WHERE id = @id;
Msg 137, Level 15, State 2, Line 2 Must declare the scalar variable "@id".
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
Dynamic SQL that cannot see the caller's variables
CREATE TABLE dbo.Customers (id INT PRIMARY KEY, name NVARCHAR(100), city NVARCHAR(50));
DECLARE @id INT = 5, @city NVARCHAR(50) = N'Leeds';
EXEC sp_executesql
N'UPDATE dbo.Customers SET city = @city WHERE id = @id;'; -- no parameters passed
Msg 137, Level 15, State 2, Line 1 Must declare the scalar variable "@city".
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
A procedure body that misspells its parameter
CREATE TABLE dbo.Customers (id INT PRIMARY KEY, name NVARCHAR(100));
GO
CREATE PROCEDURE dbo.usp_GetCustomer
@CustomerId INT
AS
SELECT name FROM dbo.Customers WHERE id = @CustId;
Msg 137, Level 15, State 2, Procedure usp_GetCustomer, Line 4 Must declare the scalar variable "@CustId".
Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.
How to prevent it
Keep each logical unit of a script (DECLARE plus the statements that use it) inside one batch, and treat GO as a hard wall for variables. If values must survive across batches, store them in a temporary table or use SESSION_CONTEXT.
For dynamic SQL, always use sp_executesql with a parameter list rather than EXEC of a concatenated string. Parameters fix 137, prevent quoting errors and protect against injection. The stored procedures and functions lesson covers parameter declarations.
Common questions
I declared the variable, so why does SQL Server say I must declare it?
The declaration is not in the same batch or scope. Look for a GO between the DECLARE and the use, check whether the statement runs inside EXEC or sp_executesql, and compare the spelling.
Can sp_executesql read variables from the outer batch?
No. Dynamic SQL runs in its own scope. Declare parameters in the second argument of sp_executesql and pass the values in the following arguments.
Why does this happen in SSMS but not in my application?
In the application the driver declares @id for you as a parameter. Pasted into SSMS, the same statement has no declaration, so add a DECLARE with a test value.
Learn more on CodeWithSQL
- Stored procedures and functions lesson
- Advanced stored procedures lesson
- SQL injection explained and how to prevent it
Official documentation
- MSSQLSERVER_137 (Microsoft Learn)
- Variables (Transact-SQL)
- SQL Server utilities statements: GO
- sp_executesql (Transact-SQL)
Last technically reviewed 23 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.