shadow1ng/fscan · error

mssql: truncated us varchar

Error message

mssql: truncated us varchar

What it means

mssqlReadUSVarChar reads a UCS-2 string encoded as a 2-byte character count followed by count*2 bytes. It throws 'truncated us varchar' when fewer than 2 bytes remain to read even the length header. It is used by both mssqlParseErrorToken and mssqlSkipUSVarError, so this error means an ERROR/INFO token's message string header is missing from the payload.

Source

Thrown at plugins/services/mssql_raw.go:398

	}
	return nil
}

func mssqlSkipLen16(payload []byte, pos int) (int, error) {
	if pos+2 > len(payload) {
		return pos, fmt.Errorf("mssql: truncated token")
	}
	size := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
	next := pos + 2 + size
	if next > len(payload) {
		return pos, fmt.Errorf("mssql: invalid token size")
	}
	return next, nil
}

func mssqlReadUSVarChar(payload []byte, pos int) (string, int, error) {
	if pos+2 > len(payload) {
		return "", pos, fmt.Errorf("mssql: truncated us varchar")
	}
	chars := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
	pos += 2
	size := chars * 2
	if pos+size > len(payload) {
		return "", pos, fmt.Errorf("mssql: invalid us varchar size")
	}
	return mssqlDecodeUCS2(payload[pos : pos+size]), pos + size, nil
}

func mssqlWritePacket(w io.Writer, packetType byte, payload []byte) error {
	if len(payload)+8 > 0xffff {
		return fmt.Errorf("mssql: packet too large")
	}
	header := []byte{packetType, tdsStatusEOM, 0, 0, 0, 0, 1, 0}
	binary.BigEndian.PutUint16(header[2:4], uint16(len(payload)+8))
	if _, err := w.Write(header); err != nil {
		return err

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Retry the connection to rule out transient truncation.
  2. Verify your TDS packet reassembly honors total packet length and continuation packets.
  3. Capture the raw stream and confirm where the payload ends relative to the string header.
  4. If parsing your own buffered payload, check the buffer was fully populated before parsing tokens.

Example fix

// before: parse token strings assuming full payload
msg, _, err := mssqlReadUSVarChar(payload, pos)
// after: verify the 2-byte header fits before calling
if pos+2 > len(payload) {
    return fmt.Errorf("cannot read error message: payload ends at %d", len(payload))
}
msg, _, err := mssqlReadUSVarChar(payload, pos)
Defensive patterns

Strategy: validation

Validate before calling

if pos+2 > len(payload) {
    return fmt.Errorf("US_VARCHAR header missing: need 2 bytes at offset %d, payload is %d", pos, len(payload))
}
chars := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
if pos+2+chars*2 > len(payload) {
    return fmt.Errorf("US_VARCHAR body of %d chars overruns payload", chars)
}

Type guard

func hasUSVarChar(payload []byte, pos int) bool {
    if pos+2 > len(payload) {
        return false
    }
    return pos+2+int(binary.LittleEndian.Uint16(payload[pos:pos+2]))*2 <= len(payload)
}

Try / catch

msg, next, err := mssqlReadUSVarChar(payload, pos)
if err != nil {
    return fmt.Errorf("reading server error message failed: %w", err)
}

Prevention

When it happens

Trigger: Either token parser positions the cursor at a US_VARCHAR whose 2-byte length header falls past the payload end (pos+2 > len(payload)).

Common situations: Server response truncated inside an ERROR/INFO token; man-in-the-middle or packet loss dropped the string header; fuzzed payloads ending mid-token; a custom packet reader assembled the login response incorrectly (e.g. wrong TDS packet reassembly).

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/e24b72878e8ea201. Report an issue: GitHub.