XTLS/Xray-core · error

Trojan fallbacks: "path" must be empty or start with "/"

Error message

Trojan fallbacks: "path" must be empty or start with "/"

What it means

Thrown while validating Trojan inbound fallbacks: any non-empty 'path' must start with '/' because it is matched as an HTTP path prefix. A path like "ws" or "api/v1" is rejected.

Source

Thrown at infra/conf/trojan.go:175

			_ = json.Unmarshal(fb.Dest, &s)
		}
		config.Fallbacks = append(config.Fallbacks, &trojan.Fallback{
			Name: fb.Name,
			Alpn: fb.Alpn,
			Path: fb.Path,
			Type: fb.Type,
			Dest: s,
			Xver: fb.Xver,
		})
	}
	for _, fb := range config.Fallbacks {
		/*
			if fb.Alpn == "h2" && fb.Path != "" {
				return nil, errors.New(`Trojan fallbacks: "alpn":"h2" doesn't support "path"`)
			}
		*/
		if fb.Path != "" && fb.Path[0] != '/' {
			return nil, errors.New(`Trojan fallbacks: "path" must be empty or start with "/"`)
		}
		if fb.Type == "" && fb.Dest != "" {
			if fb.Dest == "serve-ws-none" {
				fb.Type = "serve"
			} else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' {
				fb.Type = "unix"
				if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
					fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
					copy(fullAddr, fb.Dest[1:])
					fb.Dest = string(fullAddr)
				}
			} else {
				if _, err := strconv.Atoi(fb.Dest); err == nil {
					fb.Dest = "localhost:" + fb.Dest
				}
				if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
					fb.Type = "tcp"
				}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Prefix the path with '/': "path": "/ws"
  2. Omit "path" entirely if the fallback should catch all unmatched requests

Example fix

// before
{ "dest": 80, "path": "ws" }
// after
{ "dest": 80, "path": "/ws" }
Defensive patterns

Strategy: validation

Validate before calling

for _, fb := range fallbacks {
    p := gjson.Get(fb.Raw, "path").String()
    if p != "" && !strings.HasPrefix(p, "/") {
        return fmt.Errorf("fallback path %q must start with '/'", p)
    }
}

Prevention

When it happens

Trigger: A fallback object { "dest": 80, "path": "ws" } — missing the leading slash — in a Trojan inbound's "fallbacks" array.

Common situations: Adapting an nginx/caddy location string that omitted the slash, or copying a WebSocket path from a client share link (which often shows it without '/').

Related errors


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