{"record":{"id":"873f04210de8ba70","repo":"v2rayA/v2rayA","slug":"outboundtype-must-be-direct-or-routinga","errorCode":null,"errorMessage":"outboundType must be 'direct' or 'routingA'","messagePattern":"outboundType must be 'direct' or 'routingA'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"service/server/controller/customInbound.go","lineNumber":48,"sourceCode":"\tif ci.Port <= 0 || ci.Port > 65535 {\n\t\tcommon.ResponseError(ctx, logError(fmt.Errorf(\"invalid port\")))\n\t\treturn\n\t}\n\tif ci.Tag == \"\" {\n\t\tcommon.ResponseError(ctx, logError(fmt.Errorf(\"tag is required\")))\n\t\treturn\n\t}\n\tif net.ParseIP(\"0.0.0.0:\"+fmt.Sprint(ci.Port)) == nil {\n\t\t// basic port check already done above\n\t}\n\n\t// Validate outbound binding\n\tif ci.Outbound == \"\" {\n\t\tcommon.ResponseError(ctx, logError(fmt.Errorf(\"outbound group is required\")))\n\t\treturn\n\t}\n\tif ci.OutboundType != \"direct\" && ci.OutboundType != \"routingA\" {\n\t\tcommon.ResponseError(ctx, logError(fmt.Errorf(\"outboundType must be 'direct' or 'routingA'\")))\n\t\treturn\n\t}\n\n\t// Verify the outbound group exists\n\toutbounds := configure.GetOutbounds()\n\toutboundExists := false\n\tfor _, ob := range outbounds {\n\t\tif ob == ci.Outbound {\n\t\t\toutboundExists = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !outboundExists {\n\t\tcommon.ResponseError(ctx, logError(fmt.Errorf(\"outbound group '%s' does not exist\", ci.Outbound)))\n\t\treturn\n\t}\n\n\t// If outboundType is \"routingA\", validate the RoutingA rules","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/v2rayA/v2rayA/blob/71e5442fc548c05680ee55eae943e6c0afe9ac51/service/server/controller/customInbound.go#L30-L66","documentation":"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.","triggerScenarios":"POST /custom-inbound with ci.OutboundType not equal to \"direct\" or \"routingA\" — e.g. \"rule\", \"freedom\", \"block\", \"\", or mixed case.","commonSituations":"Passing a v2ray outbound tag name where the API expects a binding mode; typos like \"routing-a\" or \"RoutingA\".","solutions":["Set outboundType to exactly \"direct\" or \"routingA\" (lowercase)","If per-rule routing is needed, choose \"routingA\" and supply RoutingARules","Normalize the enum in the client before sending"],"exampleFix":"// before\n{\"outboundType\":\"routing-a\",...}\n// after\n{\"outboundType\":\"routingA\",...}","handlingStrategy":"validation","validationCode":"var validOutboundTypes = map[string]bool{\"direct\":true,\"routingA\":true}; if !validOutboundTypes[ci.OutboundType] { return fmt.Errorf(\"outboundType must be direct or routingA\") }","typeGuard":"func isValidOutboundType(t string) bool { return t == \"direct\" || t == \"routingA\" }","tryCatchPattern":"if err := postCustomInbound(ci); strings.Contains(err.Error(), \"outboundType must be\") { ci.OutboundType = normalizeType(ci.OutboundType); retry }","preventionTips":["Use a dropdown limited to direct/routingA","Compare case-insensitively then send the canonical value","Do not confuse outbound tags with binding modes"],"tags":["validation","api","enum","go"],"backgroundTag":"invalid-enum-value","analyzedSha":"71e5442fc548c05680ee55eae943e6c0afe9ac51","analyzedAt":"2026-09-05T20:04:37.459Z","contentChangedAt":"2026-09-05T20:04:37.459Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}