v2rayA/v2rayA · error

outboundType must be 'direct' or 'routingA'

Error message

outboundType must be 'direct' or 'routingA'

What it means

The outbound binding of a custom inbound must use outboundType "direct" or "routingA". Any other value is rejected because the config generator only supports these two binding modes for custom inbounds.

Source

Thrown at service/server/controller/customInbound.go:48

	if ci.Port <= 0 || ci.Port > 65535 {
		common.ResponseError(ctx, logError(fmt.Errorf("invalid port")))
		return
	}
	if ci.Tag == "" {
		common.ResponseError(ctx, logError(fmt.Errorf("tag is required")))
		return
	}
	if net.ParseIP("0.0.0.0:"+fmt.Sprint(ci.Port)) == nil {
		// basic port check already done above
	}

	// Validate outbound binding
	if ci.Outbound == "" {
		common.ResponseError(ctx, logError(fmt.Errorf("outbound group is required")))
		return
	}
	if ci.OutboundType != "direct" && ci.OutboundType != "routingA" {
		common.ResponseError(ctx, logError(fmt.Errorf("outboundType must be 'direct' or 'routingA'")))
		return
	}

	// Verify the outbound group exists
	outbounds := configure.GetOutbounds()
	outboundExists := false
	for _, ob := range outbounds {
		if ob == ci.Outbound {
			outboundExists = true
			break
		}
	}
	if !outboundExists {
		common.ResponseError(ctx, logError(fmt.Errorf("outbound group '%s' does not exist", ci.Outbound)))
		return
	}

	// If outboundType is "routingA", validate the RoutingA rules

View on GitHub (pinned to 71e5442fc5)

Solutions

  1. Set outboundType to exactly "direct" or "routingA" (lowercase)
  2. If per-rule routing is needed, choose "routingA" and supply RoutingARules
  3. Normalize the enum in the client before sending

Example fix

// before
{"outboundType":"routing-a",...}
// after
{"outboundType":"routingA",...}
Defensive patterns

Strategy: validation

Validate before calling

var validOutboundTypes = map[string]bool{"direct":true,"routingA":true}; if !validOutboundTypes[ci.OutboundType] { return fmt.Errorf("outboundType must be direct or routingA") }

Type guard

func isValidOutboundType(t string) bool { return t == "direct" || t == "routingA" }

Try / catch

if err := postCustomInbound(ci); strings.Contains(err.Error(), "outboundType must be") { ci.OutboundType = normalizeType(ci.OutboundType); retry }

Prevention

When it happens

Trigger: POST /custom-inbound with ci.OutboundType not equal to "direct" or "routingA" — e.g. "rule", "freedom", "block", "", or mixed case.

Common situations: Passing a v2ray outbound tag name where the API expects a binding mode; typos like "routing-a" or "RoutingA".

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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