XTLS/Xray-core · critical

`scMaxEachPostBytes` should be bigger than 0

Error message

`scMaxEachPostBytes` should be bigger than 0

What it means

The splithttp dialer panics because GetNormalizedScMaxEachPostBytes().From is <= 0 after normalization. Normalization only substitutes defaults when ScMaxEachPostBytes is nil or To == 0; a RangeConfig explicitly present with From <= 0 (or To set but From unset/negative) reaches this check and kills the process at stream-dial time. scMaxEachPostBytes caps the size of each POST upload chunk in stream-up (XHTTP) mode.

Source

Thrown at transport/internet/splithttp/dialer.go:487

			return nil, err
		}
	}
	if mode == "stream-up" {
		if xmuxClient != nil {
			xmuxClient.LeftRequests.Add(-1)
		}
		_, _, _, err = httpClient.OpenStream(ctx, requestURL.String(), sessionId, reader, true)
		if err != nil { // browser dialer only
			return nil, err
		}
		return stat.Connection(&conn), nil
	}

	scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
	scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()

	if scMaxEachPostBytes.From <= 0 {
		panic("`scMaxEachPostBytes` should be bigger than 0")
	}

	maxUploadSize := scMaxEachPostBytes.rand()
	// WithSizeLimit(0) will still allow single bytes to pass, and a lot of
	// code relies on this behavior. Subtract 1 so that together with
	// uploadWriter wrapper, exact size limits can be enforced
	// uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize - 1))
	uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(max(0, maxUploadSize-buf.Size)))

	conn.writer = uploadWriter{
		uploadPipeWriter,
		maxUploadSize,
	}

	go func() {
		var seq int64
		var lastWrite time.Time

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Set both bounds positive, e.g. {"from": 1000000, "to": 2000000}, or remove the scMaxEachPostBytes block entirely to take the built-in defaults.
  2. If you only care about a cap, keep From equal to a sane floor (e.g. 1–2 MB) — From drives the random upload-chunk size lower bound and must be >= 1.
  3. Fix config generators to omit the field rather than emit {from:0,to:0}.

Example fix

// before (config.json splithttp settings)
"scMaxEachPostBytes": { "to": 2000000 }
// after
"scMaxEachPostBytes": { "from": 1000000, "to": 2000000 }
Defensive patterns

Strategy: validation

Validate before calling

if c.ScMaxEachPostBytes != nil && (c.ScMaxEachPostBytes.From <= 0 || c.ScMaxEachPostBytes.To < c.ScMaxEachPostBytes.From) {
    return errors.New("scMaxEachPostBytes must satisfy 0 < from <= to")
}

Prevention

When it happens

Trigger: Explicitly setting "scMaxEachPostBytes": {"from": 0} or {"to": N} without a positive "from" in the splithttp transport config; negative values; configs generated programmatically that zero the From field while keeping To.

Common situations: Hand-written JSON where only "to" is set because authors assume from is optional; migrating from configs where the field was a plain integer; tools that emit 0 for unset numeric fields (proto3 default) combined with a non-zero To.

Related errors


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