-
Notifications
You must be signed in to change notification settings - Fork 475
Table-Valued Parameter validation fails during request.execute() despite matching SQL type ([email protected]) #1796
Description
Version
mssql: 12.1.0- SQL Server: SQL Server 2019 (on-prem)
- Node: 18.x (Next.js 14 app)
Summary
I’m attempting to pass two TVPs (dbo.GridFilter, dbo.GridSort) into a stored procedure from a Next.js server action. The TVP structure matches the SQL table types exactly: correct schema (dbo), column order, data types, lengths, and nullability. Binding succeeds (no errors from request.input()), and SQL Server confirms the table types and procedure parameters exist.
However, when request.execute('dbo.sp_Grid_PalletInventory') runs, SQL Server rejects the TVP with:
RequestError: Input parameter 'Filters' could not be validated
This occurs even when the connection string includes Database=myDBname; and a sanity check confirms the connection context is myDBname. The driver logs show filterTablePath: "[dbo].[GridFilter]", filterTableSchema: "dbo", and filterTableDatabase: "myDBname" immediately before binding.
Reproduction Steps
- Create user-defined table types:
CREATE TYPE dbo.GridFilter AS TABLE (
FieldName sysname NOT NULL,
Operator varchar(20) NOT NULL,
Value1 sql_variant NULL,
Value2 sql_variant NULL
);
CREATE TYPE dbo.GridSort AS TABLE (
FieldName sysname NOT NULL,
Direction varchar(4) NOT NULL CHECK (Direction IN ('ASC', 'DESC'))
);- Create a stored procedure that accepts these types:
CREATE OR ALTER PROCEDURE dbo.sp_Grid_PalletInventory
@Filters dbo.GridFilter READONLY,
@Sort dbo.GridSort READONLY,
@Page INT,
@PageSize INT,
@TotalCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
-- For repro you can simply SELECT from @Filters or @Sort
END;- Node code (simplified):
const filterTable = new sql.Table('dbo.GridFilter');
filterTable.columns.add('FieldName', sql.NVarChar(128));
filterTable.columns.add('Operator', sql.VarChar(20));
filterTable.columns.add('Value1', sql.Variant);
filterTable.columns.add('Value2', sql.Variant);
// (Same idea for GridSort)
const request = pool.request();
request.input('Filters', filterTable); // binding succeeds
request.input('Sort', sortTable);
request.input('Page', sql.Int, 0);
request.input('PageSize', sql.Int, 25);
request.output('TotalCount', sql.Int);
await request.execute('dbo.sp_Grid_PalletInventory'); // <-- fails- Logs show:
TVP Structure Comparison ... tableTypeName="GridFilter"
sqlServerColumns=[ FieldName(sysname not null), Operator(varchar(20) not null),
Value1(sql_variant null), Value2(sql_variant null) ]
ourColumns=[ FieldName(NVarChar 128 not null), Operator(VarChar 20 not null),
Value1(Variant null), Value2(Variant null) ]
Stored procedure parameter definitions ... ["@Filters" -> GridFilter, "@Sort" -> GridSort]
Binding TVP parameters ... success
Executing stored procedure ... Input parameter 'Filters' could not be validated
Expected behavior
SQL Server should accept the TVP, or at least return a more specific error if there’s a mismatch.
Actual behavior
RequestError: Input parameter 'Filters' could not be validated is thrown at request.execute. There are no validation errors during binding. The same stored procedure works when called from SSMS or other clients using the TVPs directly.
What I’ve tried
- Verified connection uses
Database=myDBname; - Confirmed table types and procedure exist in
myDBname - Ensured column order, types, lengths, nullability match exactly
- Checked that
filterTable.schema = 'dbo',filterTable.path = '[dbo].[GridFilter]' - Tried both schema-qualified (
'dbo.GridFilter') and plain ('GridFilter'+table.schema='dbo') table constructors - Tested with and without
sql.TVPinrequest.input(docs say it should detect automatically) - Added extensive logging to confirm the driver sees the correct metadata
- No filters rows (0 rows) vs. multiple rows
- Running on
[email protected]
Question
Is there a known issue with TVP serialization in [email protected], or anything else I should configure so SQL Server accepts the TVP during execute()? Happy to provide more logging or a reproducer if needed.