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_' + @Suffixfails with Msg 102, incorrect syntax near+.@Id = (SELECT MAX(Id) FROM dbo.T)fails with a syntax error near(orSELECT.
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.