Skip to content
Microsoft SQL Server

SQL Server Error 137

Must declare the scalar variable

Applies to: All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Last technically reviewed 23 October 2026 on SQL Server 2025 (17.0.1145.4)
The full message
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "@id".
Other forms of this message
Assigning to an undeclared variable with SET (reproduced as state 1)
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "@total".
Sibling error 1087 when the undeclared variable is a table variable (reproduced)
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "@Totals".
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
Last reviewed
23 October 2026

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.

What the state number tells you
StateMeaning
1Observed when assigning to an undeclared variable with SET.
2Observed 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';
What to look for

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;
What to look for

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-only

Remove 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 data

Declare 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 schema

Correct 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;
Result
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
Result
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;
Result
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

Official documentation

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.

Still stuck on a SQL Server error?

Browse the full SQL Server error library, or get hands-on help from an experienced SQL professional.