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
- Make "extra" a JSON object mirroring SplitHTTPConfig field names/types: "extra": { "xPaddingBytes": { "from": 100, "to": 1000 } }
- Verify every nested type: ranges are objects with from/to, headers is an object of string->string, xmux is an object
- Lint the config with jq to confirm extra parses as an object
- 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
- "extra" must be a plain JSON object, never a string or array
- Mirror the exact field names/types of the top-level splithttp settings
- Do not double-encode extra as a quoted JSON string
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
- unknown format of a string list:
- invalid address:
- Invalid integer range, expected either string of form "1-2"
- invalid fakedns config
- unsupported mode:
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/882a82aa75ddeffb.
Report an issue: GitHub.