# Buggy code
def execute(cls, ctx, op):
    (a,), device_id, xp = as_same_device(
        [ctx[c.key] for c in op.inputs], device=op.device, ret_extra=True
    )

    with device(device_id):
        n = op.n_partition
        w = a.shape[op.axis] * 1.0 / (n + 1)
        if not op.return_indices:
            if op.kind is not None:
                # sort
                res = ctx[op.outputs[0].key] = _sort(a, op, xp)
            else:
                # do not sort, prepare for sample by `xp.partition`
                kth = xp.linspace(
                    max(w - 1, 0), a.shape[op.axis] - 1, num=n, endpoint=False
                ).astype(int)
                ctx[op.outputs[0].key] = res = xp.partition(
                    a, kth, axis=op.axis, order=op.order
                )
        else:
            if op.kind is not None:
                # argsort
                indices = _argsort(a, op, xp)
            else:
                # do not sort, use `xp.argpartition`
                kth = xp.linspace(
                    max(w - 1, 0), a.shape[op.axis] - 1, num=n, endpoint=False
                ).astype(int)
                indices = xp.argpartition(a, kth, axis=op.axis, order=op.order)
            ctx[op.outputs[0].key] = res = xp.take_along_axis(a, indices, op.axis)
            ctx[op.outputs[1].key] = op.axis_offset + indices

        # do regular sample
        if op.order is not None:
            res = res[op.order]
        slc = xp.linspace(
            max(w - 1, 0), a.shape[op.axis] - 1, num=n, endpoint=False
        ).astype(int)
        slc = (slice(None),) * op.axis + (slc,)
        ctx[op.outputs[-1].key] = res[slc]


# Traceback (IndexError)
In [59]: a = mt.random.randint(0, 2, size=(1000,), chunk_size=300)

In [60]: mt.sort(mt.sort(a)).execute()
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-60-6c95dab88665> in <module>
----> 1 mt.sort(mt.sort(a)).execute()

~/Documents/mars_dev/mars/mars/core.py in execute(self, session, **kw)
644
645         if wait:
--> 646             return run()
647         else:
648             thread_executor = ThreadPoolExecutor(1)

~/Documents/mars_dev/mars/mars/core.py in run()
640
641         def run():
--> 642             self.data.execute(session, **kw)
643             return self
644

~/Documents/mars_dev/mars/mars/core.py in execute(self, session, **kw)
377
378         if wait:
--> 379             return run()
380         else:
381             # leverage ThreadPoolExecutor to submit task,

~/Documents/mars_dev/mars/mars/core.py in run()
372         def run():
373             # no more fetch, thus just fire run
--> 374             session.run(self, **kw)
375             # return Tileable or ExecutableTuple itself
376             return self

~/Documents/mars_dev/mars/mars/session.py in run(self, *tileables, **kw)
503         tileables = tuple(mt.tensor(t) if not isinstance(t, (Entity, Base)) else t
504                           for t in tileables)
--> 505         result = self._sess.run(*tileables, **kw)
506
507         for t in tileables:

~/Documents/mars_dev/mars/mars/session.py in run(self, *tileables, **kw)
109             # set number of running cores
110             self.context.set_ncores(kw['n_parallel'])
--> 111             res = self._executor.execute_tileables(tileables, **kw)
112             return res
113

~/Documents/mars_dev/mars/mars/utils.py in _inner(*args, **kwargs)
456         def _inner(*args, **kwargs):
457             with self:
--> 458                 return func(*args, **kwargs)
459
460         return _inner

~/Documents/mars_dev/mars/mars/executor.py in execute_tileables(self, tileables, fetch, n_parallel, n_thread, print_progress, mock, compose, name)
884                                    n_parallel=n_parallel or n_thread,
885                                    print_progress=print_progress, mock=mock,
--> 886                                    chunk_result=chunk_result)
887
888                 # update shape of tileable and its chunks whatever it's successful or not

~/Documents/mars_dev/mars/mars/executor.py in execute_graph(self, graph, keys, n_parallel, print_progress, mock, no_intermediate, compose, retval, chunk_result)
696             print_progress=print_progress, mock=mock, mock_max_memory=self._mock_max_memory,
697             fetch_keys=fetch_keys, no_intermediate=no_intermediate)
--> 698         res = graph_execution.execute(retval)
699         self._mock_max_memory = max(self._mock_max_memory, graph_execution._mock_max_memory)
700         if mock:

~/Documents/mars_dev/mars/mars/executor.py in execute(self, retval)
577         # wait until all the futures completed
578         for future in executed_futures:
--> 579             future.result()
580
581         if retval:

~/miniconda3/envs/py3.7.2/lib/python3.7/concurrent/futures/_base.py in result(self, timeout)
423                 raise CancelledError()
424             elif self._state == FINISHED:
--> 425                 return self.__get_result()
426
427             self._condition.wait(timeout)

~/miniconda3/envs/py3.7.2/lib/python3.7/concurrent/futures/_base.py in __get_result(self)
382     def __get_result(self):
383         if self._exception:
--> 384             raise self._exception
385         else:
386             return self._result

~/miniconda3/envs/py3.7.2/lib/python3.7/concurrent/futures/thread.py in run(self)
55
56         try:
---> 57             result = self.fn(*self.args, **self.kwargs)
58         except BaseException as exc:
59             self.future.set_exception(exc)

~/Documents/mars_dev/mars/mars/utils.py in _inner(*args, **kwargs)
456         def _inner(*args, **kwargs):
457             with self:
--> 458                 return func(*args, **kwargs)
459
460         return _inner

~/Documents/mars_dev/mars/mars/executor.py in _execute_operand(self, op)
444             # so we pass the first operand's first output to Executor.handle
445             first_op = ops[0]
--> 446             self.handle_op(first_op, results, self._mock)
447
448             # update maximal memory usage during execution

~/Documents/mars_dev/mars/mars/executor.py in handle_op(self, *args, **kw)
376
377     def handle_op(self, *args, **kw):
--> 378         return Executor.handle(*args, **kw)
379
380     def _order_starts(self):

~/Documents/mars_dev/mars/mars/executor.py in handle(cls, op, results, mock)
647                 # The `UFuncTypeError` was introduced by numpy#12593 since v1.17.0.
648                 try:
--> 649                     return runner(results, op)
650                 except UFuncTypeError as e:
651                     raise TypeError(str(e)).with_traceback(sys.exc_info()[2]) from None

~/Documents/mars_dev/mars/mars/tensor/base/psrs.py in execute(cls, ctx, op)
426                               num=n, endpoint=False).astype(int)
427             slc = (slice(None),) * op.axis + (slc,)
--> 428             ctx[op.outputs[-1].key] = res[slc]
429
430

IndexError: index 0 is out of bounds for axis 0 with size 0

# Fixed code
def execute(cls, ctx, op):
    (a,), device_id, xp = as_same_device(
        [ctx[c.key] for c in op.inputs], device=op.device, ret_extra=True
    )

    if len(a) == 0:
        # when chunk is empty, return the empty chunk itself
        ctx[op.outputs[0].key] = ctx[op.outputs[-1].key] = a
        return

    with device(device_id):
        n = op.n_partition
        w = a.shape[op.axis] * 1.0 / (n + 1)
        if not op.return_indices:
            if op.kind is not None:
                # sort
                res = ctx[op.outputs[0].key] = _sort(a, op, xp)
            else:
                # do not sort, prepare for sample by `xp.partition`
                kth = xp.linspace(
                    max(w - 1, 0), a.shape[op.axis] - 1, num=n, endpoint=False
                ).astype(int)
                ctx[op.outputs[0].key] = res = xp.partition(
                    a, kth, axis=op.axis, order=op.order
                )
        else:
            if op.kind is not None:
                # argsort
                indices = _argsort(a, op, xp)
            else:
                # do not sort, use `xp.argpartition`
                kth = xp.linspace(
                    max(w - 1, 0), a.shape[op.axis] - 1, num=n, endpoint=False
                ).astype(int)
                indices = xp.argpartition(a, kth, axis=op.axis, order=op.order)
            ctx[op.outputs[0].key] = res = xp.take_along_axis(a, indices, op.axis)
            ctx[op.outputs[1].key] = op.axis_offset + indices

        # do regular sample
        if op.order is not None:
            res = res[op.order]
        slc = xp.linspace(
            max(w - 1, 0), a.shape[op.axis] - 1, num=n, endpoint=False
        ).astype(int)
        slc = (slice(None),) * op.axis + (slc,)
        ctx[op.outputs[-1].key] = res[slc]