v2rayA/v2rayA · error

bad request: unsupported touch type

Error message

bad request: unsupported touch type

What it means

Import() validates the TYPE of the target (server/subscription) before modifying it. If which.TYPE is neither configure.ServerType nor configure.SubscriptionServerType, modification is refused with this bad-request error. It is an input-validation guard against modifying entities the import API does not support.

Source

Thrown at service/server/service/import.go:70

	urlLower := strings.ToLower(url)
	if PluginManagerValidateLink(url) || common.HasAnyPrefix(urlLower, supportedPrefix) {
		log.Trace("Import: url matches supported prefixes or is valid for PluginManager")
		var obj serverObj.ServerObj
		obj, err = ResolveURL(url)
		if err != nil {
			log.Warn("ResolveURL failed: %v", err)
			return
		}
		if which != nil && which.ID > 0 {
			// the request is to modify a server
			ind := which.ID - 1
			log.Info("Import: modifying server. ind=%v, which.ID=%v, which.TYPE=%v", ind, which.ID, which.TYPE)
			if which.TYPE != configure.ServerType {
				// Also support modifying subscription servers if the frontend allows it
				// but for now, we primarily care about ServerType
				if which.TYPE != configure.SubscriptionServerType {
					log.Warn("Import: unsupported touch type for modification: %v", which.TYPE)
					return fmt.Errorf("bad request: unsupported touch type")
				}
			}

			if which.TYPE == configure.ServerType && (ind < 0 || ind >= configure.GetLenServers()) {
				log.Warn("Import: invalid server index: %v", ind)
				return fmt.Errorf("bad request: invalid index")
			}

			if err = configure.SetServer(ind, &configure.ServerRaw{ServerObj: obj}); err != nil {
				log.Warn("Import: SetServer failed: %v", err)
				return
			}
			log.Info("Import: SetServer success for index %v", ind)
			css := configure.GetConnectedServers()
			if css.Len() > 0 {
				for _, cs := range css.Get() {
					if which.TYPE == cs.TYPE && which.ID == cs.ID {
						log.Info("Import: updating connected v2ray config")

View on GitHub (pinned to 71e5442fc5)

Solutions

  1. Set which.TYPE to configure.ServerType (or SubscriptionServerType) before calling Import
  2. If importing into an existing server, fetch the target via configure and copy its TYPE rather than constructing one manually
  3. Fix the frontend/API client to send the correct touch type field

Example fix

// before
which := configure.Which{ID: 1, TYPE: configure.SubscriptionType}
err := service.Import(which, 0, obj)
// after
which := configure.Which{ID: 1, TYPE: configure.ServerType}
err := service.Import(which, 0, obj)
Defensive patterns

Strategy: validation

Validate before calling

func validTouchType(t configure.TouchType) bool {
    return t == configure.ServerType || t == configure.SubscriptionServerType
}
if !validTouchType(which.TYPE) {
    return fmt.Errorf("refusing to import: unsupported touch type %v", which.TYPE)
}

Type guard

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

Prevention

When it happens

Trigger: Calling Import (via initConfigure or the PostImport API) with a Which struct whose TYPE field is an unsupported value, e.g. SubscriptionType or a zero/unset TYPE, instead of ServerType or SubscriptionServerType.

Common situations: Frontend bugs sending the wrong touch type; API clients hand-crafting import requests and forgetting to set TYPE; schema/version changes where new TYPE values were added but Import was not updated.

Related errors


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