shadow1ng/fscan · error

mssql: invalid error token size

Error message

mssql: invalid error token size

What it means

mssqlParseErrorToken read the 2-byte length header of a TDS ERROR token and found it structurally impossible: the token size is smaller than the mandatory 6-byte fixed header, or the token extends past the end of the payload, or the fixed header would cross the payload end. The library throws this instead of parsing out-of-bounds, because the token is malformed and cannot be trusted.

Source

Thrown at plugins/services/mssql_raw.go:340

				return false, fmt.Errorf("mssql: truncated done token")
			}
			status := binary.LittleEndian.Uint16(payload[pos : pos+2])
			return status&(tdsDoneError|tdsDoneSrvError) == 0, nil
		default:
			return false, fmt.Errorf("mssql: unexpected login token 0x%02x", token)
		}
	}
	return false, nil
}

func mssqlParseErrorToken(payload []byte, pos int) (mssqlRawError, int, error) {
	if pos+2 > len(payload) {
		return mssqlRawError{}, pos, fmt.Errorf("mssql: truncated error token")
	}
	size := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
	end := pos + 2 + size
	if size < 6 || end > len(payload) || pos+8 > len(payload) {
		return mssqlRawError{}, pos, fmt.Errorf("mssql: invalid error token size")
	}
	pos += 2
	number := int32(binary.LittleEndian.Uint32(payload[pos : pos+4]))
	pos += 4
	pos += 2
	message, next, err := mssqlReadUSVarChar(payload, pos)
	if err != nil {
		return mssqlRawError{}, pos, err
	}
	return mssqlRawError{number: number, message: message}, end, mssqlEnsureSkipBVarStrings(payload, next, end)
}

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

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Retry the login; if reproducible, capture the packet and inspect the ERROR token length field.
  2. Verify there is no proxy/VPN mangling the TDS stream between client and server.
  3. If the payload comes from your own code, log it hex-dumped and check the token framing against the TDS spec.
  4. Treat the server as untrusted/misbehaving and abort the connection rather than retrying blindly.

Example fix

// before: blindly trust the declared size
size := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
end := pos + 2 + size
// after: validate before slicing
size := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
end := pos + 2 + size
if size < 6 || end > len(payload) {
    return fmt.Errorf("mssql: invalid error token size (size=%d, payload=%d)", size, len(payload))
}
Defensive patterns

Strategy: validation

Validate before calling

size := int(binary.LittleEndian.Uint16(payload[pos : pos+2]))
if size < 6 || pos+2+size > len(payload) {
    return fmt.Errorf("ERROR token size %d invalid for payload %d", size, len(payload))
}

Try / catch

if _, _, err := mssqlParseErrorToken(payload, pos); err != nil {
    conn.Close()
    return fmt.Errorf("server sent malformed ERROR token: %w", err)
}

Prevention

When it happens

Trigger: mssqlParseLoginTokens encounters an ERROR token whose declared length < 6, or whose end = pos+2+size exceeds len(payload), or pos+8 > len(payload).

Common situations: A middlebox reassembles TCP segments incorrectly and splices token bytes; a hostile/fuzzed server sends an under-sized length to probe the parser; version mismatches between server and a protocol-shifting proxy corrupt token boundaries.

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/4bf38f8aa49807a0. Report an issue: GitHub.