XTLS/Xray-core · error · errors.Error

can't get inbound proxy from handler.

Error message

can't get inbound proxy from handler.

What it means

Returned by getInbound() in app/proxyman/command/command.go:32 when an inbound handler passed to an AlterInbound / GetInboundUsers style operation does not implement the proxy.GetInbound interface. Every inbound handler built by Xray's own proxyman implements GetInbound (exposing its inner proxy.Inbound), so a failure means a non-standard or dynamic handler was registered under that tag.

Source

Thrown at app/proxyman/command/command.go:32

	grpc "google.golang.org/grpc"
)

// InboundOperation is the interface for operations that applies to inbound handlers.
type InboundOperation interface {
	// ApplyInbound applies this operation to the given inbound handler.
	ApplyInbound(context.Context, inbound.Handler) error
}

// OutboundOperation is the interface for operations that applies to outbound handlers.
type OutboundOperation interface {
	// ApplyOutbound applies this operation to the given outbound handler.
	ApplyOutbound(context.Context, outbound.Handler) error
}

func getInbound(handler inbound.Handler) (proxy.Inbound, error) {
	gi, ok := handler.(proxy.GetInbound)
	if !ok {
		return nil, errors.New("can't get inbound proxy from handler.")
	}
	return gi.GetInbound(), nil
}

// ApplyInbound implements InboundOperation.
func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler inbound.Handler) error {
	p, err := getInbound(handler)
	if err != nil {
		return err
	}
	um, ok := p.(proxy.UserManager)
	if !ok {
		return errors.New("proxy is not a UserManager")
	}
	mUser, err := op.User.ToMemoryUser()
	if err != nil {
		return errors.New("failed to parse user").Base(err)
	}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Confirm the tag in the API request refers to a real user-facing inbound (e.g. vmess/vless/trojan inbound), not an app-internal handler.
  2. List inbounds via ListInbounds to see which tags exist and which proxy each uses.
  3. If writing a custom inbound handler, implement proxy.GetInbound's GetInbound() method.

Example fix

// before — tag points at an app-internal handler
client.AlterInbound(ctx, &AlterInboundRequest{Tag: "metrics_out", ...})

// after — use a real inbound tag
client.AlterInbound(ctx, &AlterInboundRequest{Tag: "vmss-in", ...})
Defensive patterns

Strategy: type-guard

Type guard

// check the handler exposes its inbound proxy before applying an inbound operation
if _, ok := handler.(proxy.GetInbound); !ok {
    return errors.New("handler does not expose an inbound proxy")
}

Try / catch

if err := op.ApplyInbound(ctx, handler); err != nil {
    if strings.Contains(err.Error(), "can't get inbound proxy") { skipHandler(handler.Tag()) } else { return err }
}

Prevention

When it happens

Trigger: Calling handlerServer.AlterInbound or GetInboundUsers where the tag resolves to a handler type that is not a standard proxyman inbound — for example a handler injected by another app (metrics' virtual outbound-style handler, or a commander-registered handler).

Common situations: API users driving the HandlerService API (AddUser/RemoveUser/GetInboundUsers) against a tag that belongs to an app-internal listener such as the metrics endpoint, or against an inbound created by a custom plugin that did not implement proxy.GetInbound.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/b70118131ce3760d. Report an issue: GitHub.