XTLS/Xray-core · error

Failed to unmarshal "extra".

Error message

Failed to unmarshal "extra".

What it means

SplitHTTPConfig.Build() found a non-null "extra" field (json.RawMessage at transport_method.go:287) but json.Unmarshal of its bytes into another SplitHTTPConfig failed at line 310. "extra" is a merge mechanism: settings nested under "extra" override the top-level ones (except host/path/mode, which are re-forced from the outer config at lines 313-315). The error means those bytes are not valid JSON shaped like a splithttp settings object.

Source

Thrown at infra/conf/transport_method.go:311

	CMaxReuseTimes   Int32Range `json:"cMaxReuseTimes"`
	HMaxRequestTimes Int32Range `json:"hMaxRequestTimes"`
	HMaxReusableSecs Int32Range `json:"hMaxReusableSecs"`
	HKeepAlivePeriod int64      `json:"hKeepAlivePeriod"`
}

func newRangeConfig(input Int32Range) *splithttp.RangeConfig {
	return &splithttp.RangeConfig{
		From: input.From,
		To:   input.To,
	}
}

// Build implements Buildable.
func (c *SplitHTTPConfig) Build() (proto.Message, error) {
	if c.Extra != nil {
		var extra SplitHTTPConfig
		if err := json.Unmarshal(c.Extra, &extra); err != nil {
			return nil, errors.New(`Failed to unmarshal "extra".`).Base(err)
		}
		extra.Host = c.Host
		extra.Path = c.Path
		extra.Mode = c.Mode
		c = &extra
	}

	switch c.Mode {
	case "":
		c.Mode = "auto"
	case "auto", "packet-up", "stream-up", "stream-one":
	default:
		return nil, errors.New("unsupported mode: " + c.Mode)
	}

	// Priority (client): host > serverName > address
	for k := range c.Headers {
		if strings.ToLower(k) == "host" {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Make "extra" a JSON object mirroring SplitHTTPConfig field names/types: "extra": { "xPaddingBytes": { "from": 100, "to": 1000 } }
  2. Verify every nested type: ranges are objects with from/to, headers is an object of string->string, xmux is an object
  3. Lint the config with jq to confirm extra parses as an object
  4. Drop "extra" entirely — it is optional; set tuning knobs at top level instead

Example fix

// before
"extra": "{\"xPaddingBytes\":{\"from\":100}}"
// after
"extra": { "xPaddingBytes": { "from": 100, "to": 1000 } }
Defensive patterns

Strategy: validation

Validate before calling

// Go: pre-validate that "extra" unmarshals into the splithttp settings shape
if cfg.Extra != nil {
	var probe map[string]json.RawMessage
	if err := json.Unmarshal(cfg.Extra, &probe); err != nil {
		return fmt.Errorf("extra must be a JSON object: %w", err)
	}
	if v, ok := probe["xPaddingBytes"]; ok {
		var r struct{ From, To int32 }
		if json.Unmarshal(v, &r) != nil {
			return errors.New("extra.xPaddingBytes must be {\"from\":n,\"to\":n}")
		}
	}
}

Try / catch

if err := splithttpConf.Build(); err != nil {
	if strings.Contains(err.Error(), `Failed to unmarshal "extra"`) {
		// re-run json.Unmarshal on Extra locally to get the precise offset/type error
	}
}

Prevention

When it happens

Trigger: Any splithttp transportSettings containing "extra": <value> where value is not unmarshalable into SplitHTTPConfig: a JSON string or number ("extra": "foo"), a JSON array, malformed JSON fragment, or an object whose fields have wrong types ("xPaddingBytes": 100 instead of {"from":..,"to":..}, "headers": [..] instead of an object, "xmux": true).

Common situations: Users migrating splithttp tuning knobs into "extra" after reading third-party guides; accidental double-encoded JSON (a quoted string containing JSON); copy-paste from YAML-converted configs where nesting got flattened; trailing commas inside extra.

Related errors


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