# Buggy code
def tile(cls, op):
    a = op.input
    repeats = op.repeats
    axis = op.axis
    ax = axis or 0
    out = op.outputs[0]

    check_chunks_unknown_shape(op.inputs, TilesError)

    if isinstance(repeats, TENSOR_TYPE):
        a, repeats = unify_chunks(a, (repeats, (ax,)))

    nsplit = a.nsplits[axis or 0]

    if isinstance(repeats, Integral):
        new_nsplit = []
        for split in nsplit:
            s = max(split // repeats, 1)
            c = split // s
            new_nsplit.extend([s] * c)
            if split % s != 0:
                new_nsplit.append(split % s)

        a = a.rechunk({ax: new_nsplit})._inplace_tile()

    out_chunks = []
    ax_cum_count = np.cumsum((0,) + a.nsplits[ax])
    is_repeats_ndarray = isinstance(repeats, np.ndarray)
    for out_idx in itertools.product(*[range(len(s)) for s in a.nsplits]):
        in_chunk = a.cix[out_idx]
        ax_idx = out_idx[ax]
        if is_repeats_ndarray:
            start = ax_cum_count[ax_idx]
            stop = ax_cum_count[ax_idx + 1]
            rp = repeats[start:stop]
            size = rp.sum()
        elif not isinstance(repeats, Integral):
            rp = repeats.cix[ax_idx,]
            size = np.nan
        else:
            rp = repeats
            size = in_chunk.shape[ax] * rp

        chunk_inputs = [in_chunk]
        if isinstance(rp, TENSOR_CHUNK_TYPE):
            chunk_inputs.append(rp)

        chunk_shape = in_chunk.shape[:ax] + (size,) + in_chunk.shape[ax + 1 :]
        chunk_op = op.copy().reset_key()
        if len(chunk_inputs) < 2:
            # repeats is not chunk
            chunk_op._repeats = rp
        out_chunk = chunk_op.new_chunk(
            chunk_inputs, shape=chunk_shape, index=out_idx, order=out.order
        )
        out_chunks.append(out_chunk)

    nsplits = [
        tuple(
            c.shape[i]
            for c in out_chunks
            if all(idx == 0 for j, idx in enumerate(c.index) if j != i)
        )
        for i in range(len(out_chunks[0].shape))
    ]
    new_op = op.copy()
    return new_op.new_tensors(
        op.inputs, out.shape, order=out.order, chunks=out_chunks, nsplits=nsplits
    )


# Traceback (AttributeError)
In [3]: import pandas as pd

In [4]: data = pd.DataFrame(np.arange(20).reshape((4, 5)) + 1, columns=['a', 'b', 'c', 'd', 'e'])

In [6]: df = md.DataFrame(data)

In [7]: df.groupby(['a','b']).size().execute()
Unexpected exception occurred in enter_build_mode.<locals>.inner.
Traceback (most recent call last):
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 353, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 493, in inner
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/scheduler/graph.py", line 633, in prepare_graph
self._target_tileable_datas + fetch_tileables, tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 399, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 493, in inner
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 350, in build
tileables, tileable_graph=tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 399, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 493, in inner
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 263, in build
self._on_tile_failure(tileable_data.op, exc_info)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 302, in inner
raise exc_info[1].with_traceback(exc_info[2]) from None
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 243, in build
tiled = self._tile(tileable_data, tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 338, in _tile
return super()._tile(tileable_data, tileable_graph)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 203, in _tile
tds = on_tile(tileable_data.op.outputs, tds)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/scheduler/graph.py", line 615, in on_tile
return self.context.wraps(handler.dispatch)(first.op)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/context.py", line 69, in h
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/utils.py", line 399, in _wrapped
return func(*args, **kwargs)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/tiles.py", line 119, in dispatch
tiled = op_cls.tile(op)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/groupby/aggregation.py", line 481, in tile
return cls._tile_with_tree(op)
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/groupby/aggregation.py", line 412, in _tile_with_tree
index = out_df.index_value.to_pandas()
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/core.py", line 283, in to_pandas
return self._index_value.to_pandas()
File "/Users/hekaisheng/Documents/mars_dev/mars/mars/dataframe/core.py", line 197, in to_pandas
sortorder=self._sortorder, names=self._names)
AttributeError: _sortorder

# Fixed code
def tile(cls, op):
    a = op.input
    repeats = op.repeats
    axis = op.axis
    ax = axis or 0
    out = op.outputs[0]

    check_chunks_unknown_shape(op.inputs, TilesError)

    if isinstance(repeats, TENSOR_TYPE):
        a, repeats = unify_chunks(a, (repeats, (ax,)))

    nsplit = a.nsplits[axis or 0]

    if isinstance(repeats, Integral):
        new_nsplit = []
        for split in nsplit:
            s = max(split // repeats, 1)
            c = split // s
            new_nsplit.extend([s] * c)
            if split % s != 0:
                new_nsplit.append(split % s)

        a = a.rechunk({ax: new_nsplit})._inplace_tile()

    out_chunks = []
    ax_cum_count = np.cumsum((0,) + a.nsplits[ax])
    is_repeats_ndarray = isinstance(repeats, np.ndarray)
    for out_idx in itertools.product(*[range(len(s)) for s in a.nsplits]):
        in_chunk = a.cix[out_idx]
        ax_idx = out_idx[ax]
        if is_repeats_ndarray:
            start = ax_cum_count[ax_idx]
            stop = ax_cum_count[ax_idx + 1]
            rp = repeats[start:stop]
            size = int(rp.sum())
        elif not isinstance(repeats, Integral):
            rp = repeats.cix[ax_idx,]
            size = np.nan
        else:
            rp = repeats
            size = in_chunk.shape[ax] * rp

        chunk_inputs = [in_chunk]
        if isinstance(rp, TENSOR_CHUNK_TYPE):
            chunk_inputs.append(rp)

        chunk_shape = in_chunk.shape[:ax] + (size,) + in_chunk.shape[ax + 1 :]
        chunk_op = op.copy().reset_key()
        if len(chunk_inputs) < 2:
            # repeats is not chunk
            chunk_op._repeats = rp
        out_chunk = chunk_op.new_chunk(
            chunk_inputs, shape=chunk_shape, index=out_idx, order=out.order
        )
        out_chunks.append(out_chunk)

    nsplits = [
        tuple(
            c.shape[i]
            for c in out_chunks
            if all(idx == 0 for j, idx in enumerate(c.index) if j != i)
        )
        for i in range(len(out_chunks[0].shape))
    ]
    new_op = op.copy()
    return new_op.new_tensors(
        op.inputs, out.shape, order=out.order, chunks=out_chunks, nsplits=nsplits
    )