# Buggy code
def _transform_general(
    self, func, *args, engine="cython", engine_kwargs=None, **kwargs
):
    """
    Transform with a non-str `func`.
    """

    if maybe_use_numba(engine):
        numba_func, cache_key = generate_numba_func(
            func, engine_kwargs, kwargs, "groupby_transform"
        )

    klass = type(self._selected_obj)

    results = []
    for name, group in self:
        object.__setattr__(group, "name", name)
        if maybe_use_numba(engine):
            values, index = split_for_numba(group)
            res = numba_func(values, index, *args)
            if cache_key not in NUMBA_FUNC_CACHE:
                NUMBA_FUNC_CACHE[cache_key] = numba_func
        else:
            res = func(group, *args, **kwargs)

        if isinstance(res, (ABCDataFrame, ABCSeries)):
            res = res._values

        indexer = self._get_index(name)
        ser = klass(res, indexer)
        results.append(ser)

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

        result = concat(results).sort_index()
    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)
    dtype = self._selected_obj.dtype
    if is_numeric_dtype(dtype):
        result = maybe_downcast_to_dtype(result, dtype)

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


# Traceback (ValueError)
In [1]: import pandas as pd
In [2]: df = pd.DataFrame({"A": [0, 0, 1, None], "B": [1, 2, 3, None]})
In [3]: gb = df.groupby("A", dropna=False)
In [6]: gb['B'].transform(len)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-6-3bae7d67a46f> in <module>
----> 1 gb['B'].transform(len)

~/sandbox/pandas/pandas/core/groupby/generic.py in transform(self, func, engine, engine_kwargs, *args, **kwargs)
471         if not isinstance(func, str):
472             return self._transform_general(
--> 473                 func, *args, engine=engine, engine_kwargs=engine_kwargs, **kwargs
474             )
475

~/sandbox/pandas/pandas/core/groupby/generic.py in _transform_general(self, func, engine, engine_kwargs, *args, **kwargs)
537
538         result.name = self._selected_obj.name
--> 539         result.index = self._selected_obj.index
540         return result
541

~/sandbox/pandas/pandas/core/generic.py in __setattr__(self, name, value)
5141         try:
5142             object.__getattribute__(self, name)
-> 5143             return object.__setattr__(self, name, value)
5144         except AttributeError:
5145             pass

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

~/sandbox/pandas/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

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

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

# Fixed code
def _transform_general(
    self, func, *args, engine="cython", engine_kwargs=None, **kwargs
):
    """
    Transform with a non-str `func`.
    """
    if maybe_use_numba(engine):
        numba_func, cache_key = generate_numba_func(
            func, engine_kwargs, kwargs, "groupby_transform"
        )

    klass = type(self._selected_obj)

    results = []
    for name, group in self:
        object.__setattr__(group, "name", name)
        if maybe_use_numba(engine):
            values, index = split_for_numba(group)
            res = numba_func(values, index, *args)
            if cache_key not in NUMBA_FUNC_CACHE:
                NUMBA_FUNC_CACHE[cache_key] = numba_func
        else:
            res = func(group, *args, **kwargs)

        if isinstance(res, (ABCDataFrame, ABCSeries)):
            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