v2rayA/v2rayA · warning

you cannot ping a subscription

Error message

you cannot ping a subscription

What it means

Which.Ping tests connectivity to the node referenced by this Which. If the Which's TYPE is SubscriptionType — i.e. it points at a whole subscription rather than an individual server — there is nothing to ping, so Ping immediately returns this error instead of attempting a connection. It is a misuse guard: pings only make sense for concrete server entries (ServerType or SubscriptionServerType).

Source

Thrown at service/db/configure/which.go:176

	case SubscriptionServerType:
		return another.TYPE == w.TYPE &&
			another.Sub == w.Sub &&
			another.ID == w.ID &&
			another.Outbound == w.Outbound
	case ServerType:
		return another.TYPE == w.TYPE &&
			another.ID == w.ID &&
			another.Outbound == w.Outbound
	case SubscriptionType:
		return another.TYPE == w.TYPE &&
			another.ID == w.ID
	default:
		return false
	}
}
func (w *Which) Ping(timeout time.Duration) (err error) {
	if w.TYPE == SubscriptionType {
		return fmt.Errorf("you cannot ping a subscription")
	}
	tsr, err := w.LocateServerRaw()
	if err != nil {
		return
	}
	//BEGIN
	host := tsr.ServerObj.GetHostname()
	if net.ParseIP(host) == nil {
		var hosts []string
		hosts, err = resolv.LookupHost(host)
		if err != nil || len(hosts) <= 0 {
			if err != nil {
				w.Latency = err.Error()
			} else {
				w.Latency = "querying dns failed: " + host
			}
			return
		}

View on GitHub (pinned to 71e5442fc5)

Solutions

  1. Ping an individual server instead: resolve a server under the subscription (e.g. Which{TYPE: SubscriptionServerType, ID: n, Sub: subIndex}) before calling Ping.
  2. Check w.TYPE == SubscriptionType in the caller and return a friendly message or pick the first server of the subscription.
  3. If the goal is to test the subscription's address, issue an HTTP GET of the subscription URL directly, not Ping.

Example fix

// before
w := configure.Which{TYPE: configure.SubscriptionType, Sub: subIdx}
err := w.Ping(timeout)
// after
if w.TYPE == configure.SubscriptionType {
    w = configure.Which{TYPE: configure.SubscriptionServerType, Sub: subIdx, ID: 1}
}
err := w.Ping(timeout)
Defensive patterns

Strategy: validation

Validate before calling

if w.TYPE == configure.SubscriptionType {
    return errors.New("select an individual server to ping, not a subscription")
}
err := w.Ping(10 * time.Second)

Type guard

func isPingable(w *configure.Which) bool {
    return w.TYPE == configure.ServerType || w.TYPE == configure.SubscriptionServerType
}

Try / catch

if err := w.Ping(timeout); err != nil {
    if strings.Contains(err.Error(), "you cannot ping a subscription") {
        // expand subscription into its servers and ping those instead
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling w.Ping(timeout) on a Which whose TYPE field is SubscriptionType (selected in a UI list of subscriptions and passed to the ping API, or constructed with Which{TYPE: SubscriptionType}).

Common situations: Front-end passes the subscription row itself to the ping endpoint instead of one of its servers; stale saved Whiches loaded from a which list after data was re-imported so IDs/TYPEs no longer match user intent.

Related errors


AI-assisted analysis of v2rayA/v2rayA@71e5442fc5 (2026-09-05). Data as JSON: /api/errors/4e994ffba2b4f635. Report an issue: GitHub.