MHSanaei/3x-ui · warning
bad ss host:port
Error message
bad ss host:port
What it means
Returned by parseShadowsocks for a SIP002-form link (ss://base64userinfo@host:port) when, after taking the part after the last '@' and trimming trailing '/', there is no ':' at all — the parser cannot split host from port. Hostname-only or IPv6-without-brackets links are the usual culprits, since the code splits on the LAST colon and expects 'host:port' text.
Source
Thrown at internal/util/link/outbound.go:365
}
core := strings.TrimPrefix(link, "ss://")
at := strings.Index(core, "@")
if at >= 0 {
// modern
userB64 := core[:at]
hp := strings.TrimRight(core[at+1:], "/")
userInfo, err := base64DecodeFlexible(userB64)
if err != nil {
// SIP022 (2022-blake3-*) userinfo is percent-encoded, not base64.
if dec, uerr := url.QueryUnescape(userB64); uerr == nil {
userInfo = dec
} else {
userInfo = userB64 // not b64, rare
}
}
colon := strings.LastIndex(hp, ":")
if colon < 0 {
return nil, fmt.Errorf("bad ss host:port")
}
host := hp[:colon]
port, err := strconv.Atoi(hp[colon+1:])
if err != nil {
return nil, fmt.Errorf("bad 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{
"servers": []any{
map[string]any{"address": host, "port": port, "password": pass, "method": method},
},
},
}
return &ParseResult{Outbound: ob, Identity: identity}, nilView on GitHub (pinned to ad32144c42)
Solutions
- Inspect the link after '@': it must look like host:443 (or [v6host]:443).
- Wrap IPv6 hosts in square brackets and always include an explicit port.
- Re-export the link from the source client's share function.
- For batch imports, validate with a regexp like ^ss://[^@]+@[^@]+:\d+ before parsing.
Example fix
// before ss://YWVzLTI1Ni1nY206cGFzcw==@example.com // after ss://YWVzLTI1Ni1nY206cGFzcw==@example.com:8388
Defensive patterns
Strategy: validation
Validate before calling
var sip002Re = regexp.MustCompile(`^ss://[^@]+@([a-zA-Z0-9.\-]+|\[[^]]+\]):\d{1,5}`)
func looksLikeValidSIP002(link string) bool { return sip002Re.MatchString(strings.TrimSpace(link)) } Prevention
- Always include an explicit numeric port in ss links.
- Bracket IPv6 hosts: [2001:db8::1]:443.
- Validate with a regexp before bulk import.
When it happens
Trigger: Link truncated before the port ('ss://xxx@host'); IPv6 host written without square brackets followed by port, or with brackets but no ':port' after; a remark fragment swallowing the port (ss://xxx@host#remark where '/' trimming already failed); plain host with no port.
Common situations: Hand-typed or sed-edited ss links; providers emitting 'ss://method:pass@host' with the port dropped; IPv6 servers exported by clients that omit brackets in the userinfo form.
Related errors
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/0916fef3fd177c43.
Report an issue: GitHub.