shadow1ng/fscan · error
mssql: invalid prelogin option table
Error message
mssql: invalid prelogin option table
What it means
The prelogin option table walk ran past the end of the payload without ever encountering the 0xFF terminator. Every 5-byte option entry is examined; if the buffer ends before a terminator byte appears, the table is malformed.
Source
Thrown at plugins/services/mssql_raw.go:174
if len(payload) == 0 {
return fmt.Errorf("mssql: empty prelogin response")
}
fields, err := mssqlParsePreloginFields(payload)
if err != nil {
return err
}
if _, ok := fields[tdsPreloginEncryption]; !ok {
return fmt.Errorf("mssql: prelogin response missing encryption field")
}
return nil
}
func mssqlParsePreloginFields(payload []byte) (map[byte][]byte, error) {
fields := make(map[byte][]byte)
for pos := 0; ; pos += 5 {
if pos >= len(payload) {
return nil, fmt.Errorf("mssql: invalid prelogin option table")
}
token := payload[pos]
if token == tdsPreloginTerminator {
return fields, nil
}
if pos+5 > len(payload) {
return nil, fmt.Errorf("mssql: truncated prelogin option")
}
offset := int(binary.BigEndian.Uint16(payload[pos+1 : pos+3]))
length := int(binary.BigEndian.Uint16(payload[pos+3 : pos+5]))
if offset < 0 || length < 0 || offset+length > len(payload) {
return nil, fmt.Errorf("mssql: invalid prelogin option bounds")
}
fields[token] = payload[offset : offset+length]
}
}
func mssqlSendLogin7(w io.Writer, host, username, password string) error {View on GitHub (pinned to 95cc12e753)
Solutions
- Re-run the connection — transient truncation may disappear; if persistent, capture packets to find where bytes are lost.
- Verify the endpoint speaks TDS (test with sqlcmd).
- Check MTU/fragmentation issues or TLS termination proxies that mangle payloads.
- If parsing third-party TDS traffic, validate the option table length equals number-of-options*5+1.
Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the response shape client-side after mssqlReadMessage:
if len(payload) < 6 || (len(payload)-1)%5 != 0 {
// option table cannot be well-formed; skip parsing
} Type guard
func looksLikeOptionTable(payload []byte) bool {
return len(payload) > 0 && (len(payload)-1)%5 == 0 && payload[len(payload)-1] != 0xff || len(payload) > 0
} Try / catch
_, err := mssqlRawLogin(ctx, host, port, user, pass, timeout)
if err != nil && strings.Contains(err.Error(), "invalid prelogin option table") {
// treat as protocol corruption: log payload hex, reconnect or mark host unhealthy
} Prevention
- Log the hex payload on parse failure to detect truncation patterns.
- Check MTU/proxy settings when many hosts fail the same way.
- Reconnect-and-retry once before flagging the server as broken.
When it happens
Trigger: mssqlParsePreloginFields iterates pos in steps of 5 over the payload and pos >= len(payload) before a 0xFF byte is read — payload length is not compatible with a valid (5*k+1)-byte option table.
Common situations: Truncated packets from flaky networks/middleboxes; a non-TDS service emitting garbage; bug in a TDS proxy reassembling prelogin responses.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- mssql: truncated prelogin option
- mssql: invalid prelogin option bounds
- mssql: invalid prelogin response packet type %d
- mssql: empty prelogin response
- mssql: prelogin response missing encryption field
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/6b1fbc7ec731c280.
Report an issue: GitHub.