XTLS/Xray-core · error · errors.Error

unknown operation

Error message

unknown operation

What it means

Returned by handlerServer.AlterInbound (app/proxyman/command/command.go:88) when request.Operation.GetInstance() fails: the serialized Any operation message could not be turned into a concrete operation. Xray looks the operation type up in the global proto type registry; an unregistered or unknown type_url fails here.

Source

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

	ohm outbound.Manager
}

func (s *handlerServer) AddInbound(ctx context.Context, request *AddInboundRequest) (*AddInboundResponse, error) {
	if err := core.AddInboundHandler(s.s, request.Inbound); err != nil {
		return nil, err
	}

	return &AddInboundResponse{}, nil
}

func (s *handlerServer) RemoveInbound(ctx context.Context, request *RemoveInboundRequest) (*RemoveInboundResponse, error) {
	return &RemoveInboundResponse{}, s.ihm.RemoveHandler(ctx, request.Tag)
}

func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) {
	rawOperation, err := request.Operation.GetInstance()
	if err != nil {
		return nil, errors.New("unknown operation").Base(err)
	}
	operation, ok := rawOperation.(InboundOperation)
	if !ok {
		return nil, errors.New("not an inbound operation")
	}

	handler, err := s.ihm.GetHandler(ctx, request.Tag)
	if err != nil {
		return nil, errors.New("failed to get handler: ", request.Tag).Base(err)
	}

	return &AlterInboundResponse{}, operation.ApplyInbound(ctx, handler)
}

func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) {
	handlers := s.ihm.ListHandlers(ctx)
	response := &ListInboundsResponse{}
	if request.GetIsOnlyTags() {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Use serial.ToTypedMessage (Go) or the exact registered type URL when constructing the operation Any.
  2. Match client and server xray-core versions so all operation types exist on the server.
  3. Prefer the typed helpers (AddUserOperation/RemoveUserOperation) instead of hand-built Any payloads.
  4. Read the Base error to distinguish 'unknown type' from 'unmarshal failed'.

Example fix

// before
op := &anypb.Any{TypeUrl: "xray.app.proxyman.command.AddUserOperation", Value: arbitrary}

// after
addOp := &command.AddUserOperation{User: user}
op := serial.ToTypedMessage(addOp)
Defensive patterns

Strategy: validation

Validate before calling

// confirm the operation type is registered before sending
if serial.MessageType((*command.AddUserOperation)(nil)) == "" {
    return errors.New("AddUserOperation not registered in this build")
}

Try / catch

resp, err := client.AlterInbound(ctx, req)
if err != nil && strings.Contains(err.Error(), "unknown operation") {
    return fmt.Errorf("server build lacks this operation; upgrade server: %w", err)
}

Prevention

When it happens

Trigger: Sending an AlterInboundRequest whose Operation Any carries a type_url that was never registered (custom operation types not compiled into the binary), or a payload that fails to unmarshal. Also occurs with version mismatch: a newer client sending an operation type the older server build does not know.

Common situations: API clients built against different xray-core versions than the server; protobuf Any fields constructed by hand with the wrong type URL string (must match the registered proto name exactly).

Related errors


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