Guides

Fix Msg 156: incorrect syntax near the keyword 'CASE' in EXEC parameters

By Scott Coffey, Senior Database Developer. Updated 2026-10-15.

This looks reasonable, and SQL Server rejects it:

EXEC dbo.usp_PurgeEventLog
     @OlderThanDays = CASE WHEN @Env = N'PROD' THEN 90 ELSE 7 END,
     @DryRun = 1;
Msg 156, Level 15, State 1, Line 2
Incorrect syntax near the keyword 'CASE'.

The error is a parse error, so nothing in the batch runs. It is a common mistake in hand-written scripts and in AI-generated T-SQL, because CASE works almost everywhere else an expression is allowed.

Why it happens

Parameters in an EXECUTE statement accept only a constant, a variable, the keyword DEFAULT, or a variable marked OUTPUT. They do not accept expressions. That rules out CASE, function calls such as GETDATE() or DATEADD(), arithmetic like @Days * 2, string concatenation, and subqueries. The same rule produces related errors:

  • @Cutoff = DATEADD(DAY, -90, SYSDATETIME()) fails with Msg 102, incorrect syntax near (.
  • @Name = N'Job_' + @Suffix fails with Msg 102, incorrect syntax near +.
  • @Id = (SELECT MAX(Id) FROM dbo.T) fails with a syntax error near ( or SELECT.

Fix 1: assign to a variable first

This is the standard fix and the one to use by default.

DECLARE @Env           nvarchar(10) = N'PROD';
DECLARE @OlderThanDays int = CASE WHEN @Env = N'PROD' THEN 90 ELSE 7 END;

EXEC dbo.usp_PurgeEventLog
     @OlderThanDays = @OlderThanDays,
     @DryRun        = 1;

Match the variable's data type to the procedure parameter. You can check parameter types with:

SELECT p.name, TYPE_NAME(p.user_type_id) AS type_name, p.max_length, p.is_output
FROM sys.parameters AS p
WHERE p.object_id = OBJECT_ID(N'dbo.usp_PurgeEventLog')
ORDER BY p.parameter_id;

An implicit conversion from a mismatched variable can truncate strings or fail at run time, so declare the exact type rather than relying on conversion.

Fix 2: branch with IF/ELSE when the call itself differs

If the condition changes which procedure runs, or which parameters are passed, use control flow instead of trying to compute the call:

IF @Env = N'PROD'
    EXEC dbo.usp_PurgeEventLog @OlderThanDays = 90, @DryRun = 1;
ELSE
    EXEC dbo.usp_PurgeEventLog @OlderThanDays = 7,  @DryRun = 0;

This keeps each call readable and makes the production path obvious in review.

Fix 3: sp_executesql with parameters

When you are already building dynamic SQL, pass computed values as parameters to sp_executesql. The values are still computed into variables first, but the call stays parameterized, which protects against injection and lets SQL Server reuse plans:

DECLARE @Days int = CASE WHEN @Env = N'PROD' THEN 90 ELSE 7 END;
DECLARE @sql  nvarchar(max) = N'EXEC dbo.usp_PurgeEventLog @OlderThanDays = @d, @DryRun = @dr;';

EXEC sys.sp_executesql @sql,
     N'@d int, @dr bit',
     @d  = @Days,
     @dr = 1;

Do not concatenate the value into the string. N'... @OlderThanDays = ' + CAST(@Days AS nvarchar(10)) works for an integer but is the habit that leads to injection when the next value is a string.

What about the procedure body

Inside the procedure, expressions are fine. If several callers need the same logic, move the CASE into the procedure and pass the raw input instead:

CREATE OR ALTER PROCEDURE dbo.usp_PurgeEventLogForEnv
    @Env    nvarchar(10),
    @DryRun bit = 1
AS
BEGIN
    SET NOCOUNT ON; SET XACT_ABORT ON;
    DECLARE @Days int = CASE WHEN @Env = N'PROD' THEN 90 ELSE 7 END;
    EXEC dbo.usp_PurgeEventLog @OlderThanDays = @Days, @DryRun = @DryRun;
END;

Teaching your AI agent the rule

Agents repeat this error because the syntax is valid in a SELECT list. One line in your project rules prevents most of it: "EXEC parameters accept only constants, variables, or DEFAULT; compute values into a typed variable first." The rules files in SQL Rig include this along with the dry-run and transaction conventions used above.