MHSanaei/3x-ui · warning

bad legacy ss

Error message

bad legacy ss

What it means

Returned by parseShadowsocks' legacy branch: the whole ss:// payload (after the scheme) was base64-decoded successfully, but the decoded string contains no '@' separating userinfo from host:port. Legacy format is base64('method:pass@host:port'), so a missing '@' means the decoded text is not a legacy ss link at all.

Source

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

		ob := Outbound{
			"protocol": "shadowsocks",
			"tag":      remark,
			"settings": map[string]any{
				"servers": []any{
					map[string]any{"address": host, "port": port, "password": pass, "method": method},
				},
			},
		}
		return &ParseResult{Outbound: ob, Identity: identity}, nil
	}
	// legacy: whole thing b64
	dec, err := base64DecodeFlexible(core)
	if err != nil {
		return nil, err
	}
	at = strings.Index(dec, "@")
	if at < 0 {
		return nil, fmt.Errorf("bad legacy ss")
	}
	userInfo := dec[:at]
	hp := dec[at+1:]
	colon := strings.LastIndex(hp, ":")
	if colon < 0 {
		return nil, fmt.Errorf("bad legacy ss hp")
	}
	host := hp[:colon]
	port, err := strconv.Atoi(hp[colon+1:])
	if err != nil {
		return nil, fmt.Errorf("bad legacy ss port %q: %w", hp[colon+1:], err)
	}
	method, pass := splitMethodPass(userInfo)
	identity := "ss:" + method + ":" + pass + "@" + host + ":" + strconv.Itoa(port)
	ob := Outbound{
		"protocol": "shadowsocks",
		"tag":      remark,
		"settings": map[string]any{

View on GitHub (pinned to ad32144c42)

Solutions

  1. Base64-decode the payload yourself and inspect it — it must read method:password@host:port.
  2. If the link is SIP002 (has a literal '@' before base64-only userinfo), keep the '@' unencoded: ss://BASE64USER@host:port.
  3. Regenerate the link with a current client (SIP002 is the modern form).
  4. Drop broken entries when batch-importing instead of retrying them.

Example fix

// before (decoded payload has no '@')
ss://cG9ydDQ0Mw==

// after (legacy form: base64 of method:pass@host:port)
ss://YWVzLTI1Ni1nY206cGFzc0BleGFtcGxlLmNvbTo0NDM=
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeLegacySS(link string) bool {
    core := strings.TrimPrefix(strings.SplitN(link, "#", 2)[0], "ss://")
    if strings.Contains(core, "@") { return false } // SIP002 form
    dec, err := base64.RawURLEncoding.DecodeString(strings.TrimRight(core, "="))
    if err != nil { dec, err = base64.StdEncoding.DecodeString(padBase64(core)) }
    return err == nil && strings.Contains(string(dec), "@")
}

Try / catch

if err != nil && strings.Contains(err.Error(), "bad legacy ss") {
    // decoded payload lacked '@': not a legacy ss link — decode it and inspect, then skip or fix
}

Prevention

When it happens

Trigger: A ss:// link that is actually SIP002 with empty userinfo (ss://@host:port style reaching the wrong branch); payload decoding to plain JSON or free text; truncated base64 that still decodes to garbage without '@'; a remark-only payload.

Common situations: Old subscription formats mixing ssr-style and ss-style encodings; links where only the userinfo was base64'd but the branch decided otherwise; hand-crafted legacy links missing the userinfo separator.

Related errors


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