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
- 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.
- List inbounds via ListInbounds to see which tags exist and which proxy each uses.
- 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
- Only target handlers created as standard protocol inbounds.
- Custom inbound handlers should implement proxy.GetInbound.
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
- proxy is not a UserManager
- not an inbound operation
- failed to get handler:
- failed to parse user
- unknown operation
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/b70118131ce3760d.
Report an issue: GitHub.