You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
NVarChar/VarBinary max values sent without a PLP terminator when length is omitted (TVP) or non-numeric — regression in 20.3.0 #1810
Problem description
Since 20.3.0, an NVarChar or VarBinary value whose length isn't comparable to a number is written with a max TYPE_INFO and PLP header but no PLP terminator. The plainest trigger is a TVP column with no length:
const{ Request,TYPES}=require('tedious')constreq=newRequest('select v from @t',console.log)req.addParameter('t',TYPES.TVP,{name: 'probe',columns: [{name: 'v',type: TYPES.NVarChar}],// no lengthrows: [['JP1016']]})connection.execSql(req)
20.2.3
20.3.0 – 20.3.4
TVP NVarChar / VarBinary column, length omitted
✅
❌ stream ended unexpectedly
TVP NVarChar column, length: 50
✅
✅
addParameter NVarChar, length: NaN
✅
❌ stream ended unexpectedly
VarChar, length omitted (TVP) or 'max'
✅
✅
Cause:NVarChar.writeValue chooses the PLP header with parameter.length <= this.maximumLength (nvarchar.ts#L96) but writes the terminator under a separate parameter.length > this.maximumLength (#L113). For undefined/NaN, both comparisons are false. varbinary.ts has the same pattern (L84/L101). VarChar.writeValue uses if/else + writePlpValue and is fine. This looks like it came in with the write-contract refactor in #1774; the 20.2.3 generateParameterData was a single if/else.
Expected behavior
As in 20.2.3: the value is sent as a terminated max (PLP) value, or the length is rejected with a clear error.
Actual behavior
The PLP chunk is written without the trailing 00000000 terminator (the value bytes end ...68006900 instead of ...68006900 00000000), and the server rejects the request.
Error message/stack trace
RequestError: The incoming tabular data stream (TDS) protocol stream is incorrect. The stream ended unexpectedly.
at RequestTokenHandler.onErrorMessage (node_modules/tedious/lib/token/handler.js:315:21)
at Parser.run (node_modules/tedious/lib/token/token-stream-parser.js:52:35)
Any other details that can be helpful
Context: node-mssql passed length: 'max', which hits the same path. That's outside tedious's typed/documented contract (Infinity is the documented sentinel), and node-mssql now converts it on its side. The TVP and NaN cases above don't involve node-mssql at all.
Suggested fix: make the terminator part of the existing else branch (or reuse writePlpValue as VarChar does) in NVarChar and VarBinary. That restores the 20.2.3 behaviour and changes nothing for valid numeric lengths. Rejecting non-numeric lengths with an InputError might be nicer long term, but it would break callers that worked on 20.2.3.
Activity
github-actions commentedon Oct 6, 2026
🎉 This issue has been resolved in version 20.3.5 🎉
The release is available on:
Your semantic-release bot 📦🚀