Skip to content

Table type conflict with TVP error in store procedure after upgrading versions #362

@MattHoffmanSW

Description

@MattHoffmanSW

I recently upgraded our project from version 2.0.11 to version 4.5.0, and have experienced several errors running a large stored procedure with several table types and TVPs in play. We are using ODBC Driver 17 on Windows 10. The errors tend to read like this:

Error: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]The data for the table-valued parameter 6 doesn't conform to the table type of the parameter. SQL Server error is: 8169, state: 2

An example of this error is when we provide the following data:

[{
  uuid: 'A2FF83F2-963F-4553-8837-D785E20707E3',
  inspectionUuid: 'ECA07CC2-DF99-4868-999A-F14A2CC1A949',
  identifierChecklistUuid: 'D274B030-A019-4C00-8396-01E53388E5FE',
  inquiryId: '58BF1BE5-3FB7-4562-A239-27AFE621317C',
  inspectionFindingId: '420B660C-B959-4B2B-90D9-55ACB657D94D',
  parentFindingId: null,
  deadline: '',
  requirementsValue: '["58BF1BE5-3FB7-4562-A239-27AFE621317C"]',
  classificationValue: '["2CA61A42-6F25-48E3-84EF-424692E3E1CC"]',
  title: 'Specific Rights',
  text: '<p>Violation</p><p></p><p></p>',
  correctionRequired: 1,
  findingAddedBy: 'Matt Hoffman',
  findingAddedOn: '04/17/2025',
  displayOrder: '0'
}]

to the following stored procedure segment:

USE [SansWrite X v10.0.6 Dev]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[saveInspection]
	@options keyValuePairs READONLY,
	@accountSettings accountSettings READONLY,
	@inspectionAttachmentsData inspectionAttachmentsData READONLY,
	@inspectionFindingCorrectionsData inspectionFindingCorrectionsData READONLY,
	@inspectionFindingHistoryData inspectionFindingHistoryData READONLY,
	@inspectionFindingsData inspectionFindingsData READONLY,
...
...
...
AS
BEGIN
	SET XACT_ABORT ON
BEGIN TRANSACTION
SAVE TRANSACTION saveInspectionTransaction
BEGIN TRY
	SET NOCOUNT ON;
...
...
...
	insert into @existingInspectionFindingsDataUuids select uuid from inspectionFindingsData where inspectionUuid in ( select uuid from @inspectionUuids)
	insert into @newUuids select uuid from @inspectionFindingsData where uuid not in (select uuid from @existingInspectionFindingsDataUuids)	
	update inspectionFindingsData
	set uuid = s.uuid
		,inspectionUuid = s.inspectionUuid
		,identifierChecklistUuid = s.identifierChecklistUuid
		,inquiryId = s.inquiryId
		,inspectionFindingId = s.inspectionFindingId
		,parentFindingId = s.parentFindingId
		,deadline = s.deadline
		,requirementsValue = s.requirementsValue
		,classificationValue = s.classificationValue
		,title = s.title
		,text = s.text
		,correctionRequired = s.correctionRequired
		,findingAddedBy = s.findingAddedBy
		,findingAddedOn = s.findingAddedOn
		,displayOrder = s.displayOrder
		,lastUpdated = CURRENT_TIMESTAMP
	from @inspectionFindingsData s
	inner join inspectionFindingsData t
	on s.uuid = t.uuid

	insert into inspectionFindingsData select *, CURRENT_TIMESTAMP from @inspectionFindingsData where uuid in (select uuid from @newUuids)	
	delete from @newUuids
...
...
...
	COMMIT TRANSACTION
END TRY
BEGIN CATCH
	THROW
	IF @@TRANCOUNT > 0
	BEGIN
		ROLLBACK TRANSACTION saveInspectionTransaction	
	END
END CATCH
END

using the following user table type:

USE [SansWrite X v10.0.6 Dev]
GO
CREATE TYPE [dbo].[inspectionFindingsData] AS TABLE(
	[uuid] [uniqueidentifier] NOT NULL,
	[inspectionUuid] [uniqueidentifier] NOT NULL,
	[identifierChecklistUuid] [uniqueidentifier] NOT NULL,
	[inquiryId] [uniqueidentifier] NOT NULL,
	[inspectionFindingId] [uniqueidentifier] NULL,
	[parentFindingId] [uniqueidentifier] NULL,
	[deadline] [varchar](50) NULL,
	[requirementsValue] [varchar](max) NULL,
	[classificationValue] [varchar](max) NULL,
	[title] [varchar](max) NULL,
	[text] [varchar](max) NULL,
	[correctionRequired] [varchar](5) NULL,
	[findingAddedBy] [varchar](100) NULL,
	[findingAddedOn] [varchar](50) NULL,
	[displayOrder] [int] NULL
)
GO

This specific example does not work with an array of length 1, but works once another item is added to the array. Unfortunately, a different table type conflict comes up for a different TVP in that case.
The data is converted to a TVP and added to the procedure using the following code:

const tableTypeForTvp = {...tableType};
tableTypeForTvp.addRowsFromObjects(data);
const tvp = sql.TvpFromTable(tableType);
tvps.push(tvp);

const pm = connection.procedureMgr();
pm.get(storedProcedureName, proc => {
    proc.call(tvps, (err, results, output) => {

Despite the error seeming to originate from the ODBC Driver, the only modifications between a working version of this process and the version where we are seeing this error is a change from Node 14 to Node 22, and a change from msnodesqlv8 2.0.11 to 4.5.0. The data, table types, stored procedure, and driver have not been touched, outside of nullifying a single piece of data that seems to violate a length limit in TVPs.

Has there been a significant change to how TVPs and stored procedures are managed between these versions? If this behavior is intended, guidance on how to debug and resolve these errors would be appreciated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions