MHSanaei/3x-ui · error

not wireguard

Error message

not wireguard

What it means

Thrown by parseWireguard in internal/util/link/outbound.go when a link string is routed to the WireGuard parser but its URL scheme is neither 'wireguard' nor 'wg'. The parser dispatcher selects a parser by scheme (or by heuristics for scheme-less links), so this error means the dispatcher guessed WireGuard for a link that is not one. It is a pure input-classification error, not a malformed-WireGuard error.

Source

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

	ob := Outbound{
		"protocol":       "hysteria",
		"tag":            decodeHash(u.Fragment),
		"settings":       map[string]any{"address": host, "port": port, "version": 2},
		"streamSettings": stream,
	}
	return &ParseResult{Outbound: ob, Identity: identity}, nil
}

// --- wireguard ---

func parseWireguard(link string) (*ParseResult, error) {
	u, err := url.Parse(link)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "wireguard" && u.Scheme != "wg" {
		return nil, fmt.Errorf("not wireguard")
	}
	secret, _ := url.QueryUnescape(u.User.Username())
	params := u.Query()
	host := u.Hostname()
	portStr := u.Port()
	endpoint := host
	if portStr != "" {
		endpoint = host + ":" + portStr
	}

	addrRaw := firstParam(params, "address", "ip")
	allowedRaw := firstParam(params, "allowedips", "allowed_ips")
	addrs := splitComma(addrRaw)
	if len(addrs) == 0 {
		addrs = []string{"0.0.0.0/0", "::/0"}
	}
	allowed := splitComma(allowedRaw)
	if len(allowed) == 0 {

View on GitHub (pinned to ad32144c42)

Solutions

  1. Check the link scheme before calling: only pass links starting with wireguard:// or wg:// to parseWireguard.
  2. If parsing arbitrary subscription content, dispatch on the substring before '://' yourself and send non-wg links to the right parser instead of relying on heuristics.
  3. If the link came from a subscription, fetch it again and inspect the raw entries for truncation or HTML error pages.
  4. If you intended a real WireGuard link, fix the scheme prefix to wireguard:// (secret as userinfo, endpoint as host).

Example fix

// before
res, err := parseWireguard(link) // link = "ss://..." -> "not wireguard"

// after
if scheme := strings.SplitN(link, "://", 2)[0]; scheme != "wireguard" && scheme != "wg" {
    return nil, fmt.Errorf("link is %s, not wireguard", scheme)
}
res, err := parseWireguard(link)
Defensive patterns

Strategy: type-guard

Validate before calling

func isWireguardLink(link string) bool {
	s := strings.ToLower(strings.TrimSpace(link))
	return strings.HasPrefix(s, "wireguard://") || strings.HasPrefix(s, "wg://")
}

Type guard

func isWireguardLink(link string) bool {
	s := strings.ToLower(strings.TrimSpace(link))
	return strings.HasPrefix(s, "wireguard://") || strings.HasPrefix(s, "wg://")
}

Try / catch

if res, err := parseWireguard(link); err != nil {
    if strings.Contains(err.Error(), "not wireguard") {
        // route link to the correct protocol parser instead
    }
    return err
}

Prevention

When it happens

Trigger: Calling the outbound link parser (e.g. ParseOutboundLink / the generic dispatch entry point that includes parseWireguard) with a string whose scheme is something else entirely (ss://, vless://, an arbitrary URI, or bare base64) after the dispatcher selected the wireguard branch — typically because the link lacks a recognizable scheme and 'wg'-like content heuristics matched.

Common situations: Pasting a wrong subscription link type into a field that expects wireguard:// links; subscription feeds mixing protocols where one entry is truncated or corrupted; a hand-built URL like wg:// with uppercase scheme 'WG://' (url.Parse lowercases scheme, so usually fine) or a typo like 'wirguard://'.

Related errors


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