# Buggy code
def _transform_general(self, func, *args, **kwargs):
    """
    Transform with a non-str `func`.
    """
    klass = type(self._selected_obj)

    results = []
    for name, group in self:
        object.__setattr__(group, "name", name)
        res = func(group, *args, **kwargs)

        if isinstance(res, (DataFrame, Series)):
            res = res._values

        results.append(klass(res, index=group.index))

    # check for empty "results" to avoid concat ValueError
    if results:
        from pandas.core.reshape.concat import concat

        concatenated = concat(results)
        result = self._set_result_index_ordered(concatenated)
    else:
        result = self.obj._constructor(dtype=np.float64)
    # we will only try to coerce the result type if
    # we have a numeric dtype, as these are *always* user-defined funcs
    # the cython take a different path (and casting)
    if is_numeric_dtype(result.dtype):
        common_dtype = find_common_type([self._selected_obj.dtype, result.dtype])
        if common_dtype is result.dtype:
            result = maybe_downcast_numeric(result, self._selected_obj.dtype)

    result.name = self._selected_obj.name
    result.index = self._selected_obj.index
    return result


# Traceback (ValueError)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-3bae7d67a46f> in <module>
----> 1 gb['B'].transform(len)

/workspaces/pandas-arw2019/pandas/core/groupby/generic.py in transform(self, func, engine, engine_kwargs, *args, **kwargs)
487
488         if not isinstance(func, str):
--> 489             return self._transform_general(
490                 func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
491             )

/workspaces/pandas-arw2019/pandas/core/groupby/generic.py in _transform_general(self, func, engine, engine_kwargs, *args, **kwargs)
556
557         result.name = self._selected_obj.name
--> 558         result.index = self._selected_obj.index
559         return result
560

/workspaces/pandas-arw2019/pandas/core/generic.py in __setattr__(self, name, value)
5167         try:
5168             object.__getattribute__(self, name)
-> 5169             return object.__setattr__(self, name, value)
5170         except AttributeError:
5171             pass

/workspaces/pandas-arw2019/pandas/_libs/properties.pyx in pandas._libs.properties.AxisProperty.__set__()
64
65     def __set__(self, obj, value):
---> 66         obj._set_axis(self.axis, value)

/workspaces/pandas-arw2019/pandas/core/series.py in _set_axis(self, axis, labels, fastpath)
422         if not fastpath:
423             # The ensure_index call above ensures we have an Index object
--> 424             self._mgr.set_axis(axis, labels)
425
426     # ndarray compatibility

/workspaces/pandas-arw2019/pandas/core/internals/managers.py in set_axis(self, axis, new_labels)
214
215         if new_len != old_len:
--> 216             raise ValueError(
217                 f"Length mismatch: Expected axis has {old_len} elements, new "
218                 f"values have {new_len} elements"

ValueError: Length mismatch: Expected axis has 3 elements, new values have 4 elements

# Fixed code
def _transform_general(self, func, *args, **kwargs):
    """
    Transform with a non-str `func`.
    """
    klass = type(self._selected_obj)

    results = []
    for name, group in self:
        object.__setattr__(group, "name", name)
        res = func(group, *args, **kwargs)

        if isinstance(res, (DataFrame, Series)):
            res = res._values

        results.append(klass(res, index=group.index))

    # check for empty "results" to avoid concat ValueError
    if results:
        from pandas.core.reshape.concat import concat

        concatenated = concat(results)
        result = self._set_result_index_ordered(concatenated)
    else:
        result = self.obj._constructor(dtype=np.float64)
    # we will only try to coerce the result type if
    # we have a numeric dtype, as these are *always* user-defined funcs
    # the cython take a different path (and casting)
    if is_numeric_dtype(result.dtype):
        common_dtype = find_common_type([self._selected_obj.dtype, result.dtype])
        if common_dtype is result.dtype:
            result = maybe_downcast_numeric(result, self._selected_obj.dtype)

    result.name = self._selected_obj.name
    return result