ginuerzh/gost · warning
bad type
Error message
bad type
What it means
ErrBadType is a sentinel from the TLS obfs (otls) dissector, returned during Parse when a byte does not match the expected TLS record type for the current state, and during serverHandshake when the parsed TLS record's Type is not Handshake. It means the data stream does not look like valid TLS traffic.
Source
Thrown at obfs.go:320
var (
cipherSuites = []uint16{
0xc02c, 0xc030, 0x009f, 0xcca9, 0xcca8, 0xccaa, 0xc02b, 0xc02f,
0x009e, 0xc024, 0xc028, 0x006b, 0xc023, 0xc027, 0x0067, 0xc00a,
0xc014, 0x0039, 0xc009, 0xc013, 0x0033, 0x009d, 0x009c, 0x003d,
0x003c, 0x0035, 0x002f, 0x00ff,
}
compressionMethods = []uint8{0x00}
algorithms = []uint16{
0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402,
0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203,
}
tlsRecordTypes = []uint8{0x16, 0x14, 0x16, 0x17}
tlsVersionMinors = []uint8{0x01, 0x03, 0x03, 0x03}
ErrBadType = errors.New("bad type")
ErrBadMajorVersion = errors.New("bad major version")
ErrBadMinorVersion = errors.New("bad minor version")
ErrMaxDataLen = errors.New("bad tls data len")
)
const (
tlsRecordStateType = iota
tlsRecordStateVersion0
tlsRecordStateVersion1
tlsRecordStateLength0
tlsRecordStateLength1
tlsRecordStateData
)
type obfsTLSParser struct {
step uint8
state uint8
length uint16View on GitHub (pinned to a33fdbf4c9)
Solutions
- Ensure both endpoints use the same obfs method (otls) and parameters.
- Check the very first bytes the client sends are a TLS ClientHello (record type 0x16).
- If you see this during legit traffic, verify no intermediary proxy is mangling/padding the stream.
- Match library versions on client and server; record-state expectations differ across versions.
Example fix
// server config before (expects TLS-looking traffic)
dialer: {type: "obfs", obfs: "ohttp"}
// after (client sends TLS records)
dialer: {type: "obfs", obfs: "otls"} Defensive patterns
Strategy: validation
Validate before calling
// preflight: ensure first byte of a client-side stream is a TLS record type
if len(buf) > 0 {
switch buf[0] {
case 0x16, 0x14, 0x17:
// plausible TLS record
default:
return errors.New("stream does not start with a TLS record; check obfs=otls on both ends")
}
} Type guard
func isTLSRecordType(b byte) bool {
for _, t := range []byte{0x16, 0x14, 0x17} {
if b == t { return true }
}
return false
} Try / catch
n, err := dissector.Parse(conn, buf)
if errors.Is(err, dissector.ErrBadType) {
log.Printf("non-TLS traffic on otls port from %s; closing", conn.RemoteAddr())
conn.Close()
} Prevention
- Match obfs method (otls) on client and server.
- Close connections immediately on ErrBadType — the parser cannot resync.
- Alert on spikes of ErrBadType (scanner activity or config drift).
When it happens
Trigger: Parse (obfs.go:362): a record's first byte differs from the expected tlsRecordTypes entry for the current step (0x16, 0x14, 0x16, 0x17). serverHandshake (obfs.go:533): record.Type != dissector.Handshake — the client sent a non-handshake TLS record first.
Common situations: Non-TLS clients connecting to an otls-obfuscated server (scanners, wrong transport); client/server obfs mismatch (ohttp server vs otls client); truncated or corrupted streams desynchronizing the record parser; TLS library version differences sending unexpected initial records.
Related errors
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/d9c6425a7e4cf8c9.
Report an issue: GitHub.