Skip to content

Table-Valued Parameter validation fails during request.execute() despite matching SQL type ([email protected]) #1796

@Avi-E-Koenig

Description

@Avi-E-Koenig

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

  1. 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'))
);
  1. 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;
  1. 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
  1. 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.TVP in request.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions