MHSanaei/3x-ui · warning

not trojan

Error message

not trojan

What it means

Returned by parseTrojan when the parsed URL's scheme is not exactly 'trojan'. Same shape as the vless guard: ParseOutbound only sends trojan:// links here, so this fires when parseTrojan is invoked directly or a custom routing table misroutes a trojan-variant scheme (trojan-go://) into it. Pure input validation.

Source

Thrown at internal/util/link/outbound.go:300

		"settings": map[string]any{
			"address":    host,
			"port":       port,
			"id":         id,
			"flow":       params.Get("flow"),
			"encryption": firstNonEmpty(params.Get("encryption"), "none"),
		},
		"streamSettings": stream,
	}
	return &ParseResult{Outbound: ob, Identity: identity}, nil
}

func parseTrojan(link string) (*ParseResult, error) {
	u, err := url.Parse(link)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "trojan" {
		return nil, fmt.Errorf("not trojan")
	}
	pw := u.User.Username()
	host := u.Hostname()
	port := defaultPort(u.Port(), 443)
	params := u.Query()
	network := params.Get("type")
	if network == "" {
		network = "tcp"
	}
	security := params.Get("security")
	if security == "" {
		security = "tls"
	}
	stream := buildStream(network, security)
	applyTransport(stream, params)
	applySecurity(stream, params)
	applyFinalMask(stream, params)

View on GitHub (pinned to ad32144c42)

Solutions

  1. Dispatch on the exact prefix 'trojan://' and use ParseOutbound for public parsing.
  2. Give trojan-go:// links a dedicated parser — its query params differ from plain trojan.
  3. Fix substring-based prefix checks to include the '//' boundary.

Example fix

// before
case strings.HasPrefix(link, "trojan"):
    return parseTrojan(link)

// after
case strings.HasPrefix(link, "trojan://"):
    return parseTrojan(link)
Defensive patterns

Strategy: validation

Validate before calling

func isTrojanLink(link string) bool {
    u, err := url.Parse(strings.TrimSpace(link))
    return err == nil && strings.EqualFold(u.Scheme, "trojan")
}

Prevention

When it happens

Trigger: Direct calls to parseTrojan with arbitrary URLs; routing trojan-go:// links to parseTrojan; a dispatcher prefix table matching 'trojan' as a substring (e.g. matching 'trojan-go://' with HasPrefix("trojan")).

Common situations: Forks adding trojan-go support; test fixtures calling the inner parser; prefix checks that use HasPrefix(link, "trojan") without the '://' delimiter.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/1c4a21686f6807ec. Report an issue: GitHub.