How to fix Error 352 on Windows Server 2025 / Windows 11

Troubleshooting Guide: SQL Server Error 352 — Table-Valued Parameter Must Be Declared with READONLY Option

Why This Error Happens

This error occurs because SQL Server 2025 requires all table-valued parameters (TVPs) passed into stored procedures or functions to be explicitly marked with the READONLY keyword in the parameter declaration. TVPs are internally represented as read-only, memory-optimized structures, so the engine cannot modify them within the procedure body, and it enforces this at compile time. This typically surfaces after a code migration, an ORM-generated query (like Entity Framework or Dapper), or a manually written stored procedure where the READONLY clause was omitted from the parameter list.

Step-by-Step Resolution Guide

Step 1: Identify the Offending Procedure or Query

  • Open SQL Server Management Studio (SSMS) 21 or Azure Data Studio connected to your Windows Server 2025 instance.
  • Check the error message text carefully — it names the specific parameter (e.g., @MyTableParam) and the procedure/batch where it failed.
  • Run the following to locate the procedure definition:
  SELECT OBJECT_DEFINITION(OBJECT_ID('dbo.YourProcedureName'));

Step 2: Correct the Parameter Declaration

  • Locate the parameter definition in the CREATE PROCEDURE or CREATE FUNCTION statement.
  • Add the READONLY keyword immediately after the table type declaration:
  CREATE PROCEDURE dbo.usp_InsertOrders
      @OrderList dbo.OrderTableType READONLY
  AS
  BEGIN
      -- procedure body
  END
  • Do not attempt to UPDATE, INSERT, or DELETE against the TVP inside the procedure body, as this will cause a separate error even after adding READONLY.

Step 3: Update the Application/Client-Side Code

  • If the call originates from .NET, Java, or a driver such as ODBC 18/19, ensure the parameter is passed using SqlDbType.Structured and that the corresponding stored procedure signature matches the corrected READONLY declaration.
  • Redeploy or recompile the client application after regenerating any auto-generated data access layer code (common with Entity Framework Core scaffolding).

Step 4: Recompile and Redeploy the Stored Procedure

  • Drop and recreate the procedure, or use ALTER PROCEDURE with the corrected syntax:
  ALTER PROCEDURE dbo.usp_InsertOrders
      @OrderList dbo.OrderTableType READONLY
  AS
  BEGIN
      SELECT * FROM @OrderList;
  END
  • Clear the plan cache for the specific object if testing repeatedly:
  DBCC FREEPROCCACHE;

Alternative or Verification Steps

  • Verify the fix by executing the procedure manually with a sample table variable populated using the same user-defined table type — it should run without Error 352.
  • If the error persists across multiple procedures, run a metadata audit query against sys.parameters and sys.table_types to confirm no other TVP parameters in the database are missing the is_readonly flag:
  SELECT p.name, p.is_readonly, t.name AS TypeName
  FROM sys.parameters p
  JOIN sys.table_types t ON p.user_type_id = t.user_type_id;