XTLS/Xray-core · error

sessionIDTable or sessionIDLength is too small

Error message

sessionIDTable or sessionIDLength is too small

What it means

When "sessionIDTable" is set, SplitHTTPConfig.Build() resolves predefined tables via splithttp.PredefinedTable, then computes roomSize = sum over k in [from,to] of tableLen^k (transport_method.go:512-521) and requires at least 2^31 (2<<30) possible session IDs (line 414). This error means the table plus the sessionIDLength range yields too little entropy — a small character table and/or a short ID length make session IDs guessable/enumerable.

Source

Thrown at infra/conf/transport_method.go:415

	}

	if c.SessionIDPlacement != "path" && c.SessionIDKey == "" {
		switch c.SessionIDPlacement {
		case "cookie", "query":
			c.SessionIDKey = "x_session"
		case "header":
			c.SessionIDKey = "X-Session"
		}
	}

	if c.SessionIDTable != "" {
		if predefined, ok := splithttp.PredefinedTable[c.SessionIDTable]; ok {
			c.SessionIDTable = predefined
		}
		room := roomSize(len(c.SessionIDTable), c.SessionIDLength.From, c.SessionIDLength.To)
		// 2.1B possiblities should be enough
		if room.Cmp(big.NewInt(2<<30)) < 0 {
			return nil, errors.New("sessionIDTable or sessionIDLength is too small")
		}
		if c.SessionIDLength.From <= 0 {
			return nil, errors.New("sessionIDLength.from must be greater than 0")
		}
		for i := 0; i < len(c.SessionIDTable); i++ {
			if c.SessionIDTable[i] >= 0x80 {
				return nil, errors.New("sessionIDTable must contain only ASCII characters")
			}
		}
	}

	if c.SeqPlacement != "path" && c.SeqKey == "" {
		switch c.SeqPlacement {
		case "cookie", "query":
			c.SeqKey = "x_seq"
		case "header":
			c.SeqKey = "X-Seq"
		}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Set "sessionIDLength": {"from": N, "to": N} large enough: with a 64-char table, from=6 (64^6 ≈ 6.9e10) already passes
  2. Or use a bigger "sessionIDTable" string / a predefined table name resolvable in splithttp.PredefinedTable
  3. Rule of thumb: tableLen^from must reach ~2^31, so len(table) >= 2^(31/from)

Example fix

// before
"sessionIDTable": "0123456789", "sessionIDLength": { "from": 1, "to": 2 }
// after
"sessionIDTable": "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-", "sessionIDLength": { "from": 6, "to": 8 }
Defensive patterns

Strategy: validation

Validate before calling

// Go: mirror roomSize() and require >= 2^31 possibilities
func sessionRoomOK(table string, from, to int32) bool {
	base := big.NewInt(int64(len(table)))
	sum := new(big.Int)
	term := new(big.Int)
	for k := from; k <= to; k++ {
		if k < 0 {
			continue
		}
		term.Exp(base, big.NewInt(int64(k)), nil)
		sum.Add(sum, term)
	}
	return sum.Cmp(big.NewInt(2<<30)) >= 0
}

Prevention

When it happens

Trigger: E.g. sessionIDTable "0123456789" (10 chars) with sessionIDLength {from:1,to:1}: room = 10 < 2^31. Or a 2-char custom table with any practical length range; or from/to left at 0/0 (room=0 since the loop at line 516 never runs for min=0? — with min=0,max=0 it computes base^0=1). Any combination whose summed possibilities stay under ~2.1 billion fails.

Common situations: Users shortening ID length to make URLs smaller; using tiny custom tables for 'prettier' IDs; setting sessionIDTable but forgetting sessionIDLength so the default zero range yields a tiny room; must pair a long-enough table (or predefined table) with an adequate length range.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/6560506a715259fa. Report an issue: GitHub.