XTLS/Xray-core · error
VLESS fallbacks: "path" must be empty or start with "/"
Error message
VLESS fallbacks: "path" must be empty or start with "/"
What it means
Thrown by VLessInboundConfig.Build() when validating fallbacks for a VLESS inbound. Each fallback's optional "path" field must either be empty or begin with '/', because the path is matched against the URL path of the incoming HTTP request, which always starts with '/'. Any other value (e.g. "ws", "path=/ws") cannot match and is rejected at config-build time.
Source
Thrown at infra/conf/vless.go:185
_ = json.Unmarshal(fb.Dest, &s)
}
config.Fallbacks = append(config.Fallbacks, &inbound.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(`VLESS fallbacks: "alpn":"h2" doesn't support "path"`)
}
*/
if fb.Path != "" && fb.Path[0] != '/' {
return nil, errors.New(`VLESS 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
- Change the fallback path to start with '/', e.g. "path":"/ws"
- Remove "path" entirely if the fallback should catch all paths
- Double-check you are editing the fallbacks array, not the streamSettings wsSettings
Example fix
// before
"fallbacks": [{ "dest": 80, "path": "ws" }]
// after
"fallbacks": [{ "dest": 80, "path": "/ws" }] Defensive patterns
Strategy: validation
Validate before calling
func validateFallbackPaths(cfg map[string]any) error {
inbounds, _ := cfg["inbounds"].([]any)
for _, ib := range inbounds {
m, _ := ib.(map[string]any)
settings, _ := m["settings"].(map[string]any)
fbs, _ := settings["fallbacks"].([]any)
for _, f := range fbs {
fb, _ := f.(map[string]any)
if p, ok := fb["path"].(string); ok && p != "" && !strings.HasPrefix(p, "/") {
return fmt.Errorf("fallback path %q must be empty or start with /", p)
}
}
}
return nil
} Type guard
func validFallbackPath(p any) bool {
s, ok := p.(string)
return !ok || s == "" || strings.HasPrefix(s, "/")
} Prevention
- Treat fallback paths as URL paths: always begin with '/'
- Never copy wsSettings.path values verbatim into fallbacks without adding the slash
- Add a config-lint step (jq script or Go validator) to CI for generated configs
When it happens
Trigger: Config with "fallbacks":[{"dest":"80","path":"ws"}] — path set without a leading slash. Also happens when users copy a WebSocket "path" value from a transport settings block (where no slash is required) into the fallback object.
Common situations: Migrating a config from websocket transport to fallbacks; mixing up transport "path" and fallback "path" semantics; typos like "path":"//ws" or "path":"=" prefix from proxy-share link generators.
Related errors
- VLESS fallbacks: please fill in a valid value for every "des
- VLESS fallbacks: invalid PROXY protocol version, "xver" only
- VLESS reverse: "tag" can't be empty
- VLESS reverse: invalid "sniffing" config
- VLESS settings: "vnext" should have one and only one member.
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/4dd20af6f9e47133.
Report an issue: GitHub.