# Buggy code
def _align_series(self, indexer, ser: Series, multiindex_indexer: bool = False):
    """
    Parameters
    ----------
    indexer : tuple, slice, scalar
        Indexer used to get the locations that will be set to `ser`.
    ser : pd.Series
        Values to assign to the locations specified by `indexer`.
    multiindex_indexer : boolean, optional
        Defaults to False. Should be set to True if `indexer` was from
        a `pd.MultiIndex`, to avoid unnecessary broadcasting.

    Returns
    -------
    `np.array` of `ser` broadcast to the appropriate shape for assignment
    to the locations selected by `indexer`
    """
    if isinstance(indexer, (slice, np.ndarray, list, Index)):
        indexer = (indexer,)

    if isinstance(indexer, tuple):
        # flatten np.ndarray indexers
        def ravel(i):
            return i.ravel() if isinstance(i, np.ndarray) else i

        indexer = tuple(map(ravel, indexer))

        aligners = [not com.is_null_slice(idx) for idx in indexer]
        sum_aligners = sum(aligners)
        single_aligner = sum_aligners == 1
        is_frame = self.ndim == 2
        obj = self.obj

        # are we a single alignable value on a non-primary
        # dim (e.g. panel: 1,2, or frame: 0) ?
        # hence need to align to a single axis dimension
        # rather that find all valid dims

        # frame
        if is_frame:
            single_aligner = single_aligner and aligners[0]

        # we have a frame, with multiple indexers on both axes; and a
        # series, so need to broadcast (see GH5206)
        if sum_aligners == self.ndim and all(is_sequence(_) for _ in indexer):
            ser = ser.reindex(obj.axes[0][indexer[0]], copy=True)._values

            # single indexer
            if len(indexer) > 1 and not multiindex_indexer:
                len_indexer = len(indexer[1])
                ser = np.tile(ser, len_indexer).reshape(len_indexer, -1).T

            return ser

        for i, idx in enumerate(indexer):
            ax = obj.axes[i]

            # multiple aligners (or null slices)
            if is_sequence(idx) or isinstance(idx, slice):
                if single_aligner and com.is_null_slice(idx):
                    continue
                new_ix = ax[idx]
                if not is_list_like_indexer(new_ix):
                    new_ix = Index([new_ix])
                else:
                    new_ix = Index(new_ix)
                if ser.index.equals(new_ix) or not len(new_ix):
                    return ser._values.copy()

                return ser.reindex(new_ix)._values

            # 2 dims
            elif single_aligner:
                # reindex along index
                ax = self.obj.axes[1]
                if ser.index.equals(ax) or not len(ax):
                    return ser._values.copy()
                return ser.reindex(ax)._values

    elif is_scalar(indexer):
        ax = self.obj._get_axis(1)

        if ser.index.equals(ax):
            return ser._values.copy()

        return ser.reindex(ax)._values

    raise ValueError("Incompatible indexer with Series")


# Traceback (KeyError)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~/sources/official.clone/pandas/pandas/core/generic.py in _get_axis_number(cls, axis)
456         try:
--> 457             return cls._AXIS_TO_AXIS_NUMBER[axis]
458         except KeyError:

KeyError: 1

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-24-7c4b5d72ca25> in <module>
----> 1 ser.loc[0] = pd.Series([0])

~/sources/official.clone/pandas/pandas/core/indexing.py in __setitem__(self, key, value)
689
690         iloc = self if self.name == "iloc" else self.obj.iloc
--> 691         iloc._setitem_with_indexer(indexer, value, self.name)
692
693     def _validate_key(self, key, axis: int):

~/sources/official.clone/pandas/pandas/core/indexing.py in _setitem_with_indexer(self, indexer, value, name)
1634             self._setitem_with_indexer_split_path(indexer, value, name)
1635         else:
-> 1636             self._setitem_single_block(indexer, value, name)
1637
1638     def _setitem_with_indexer_split_path(self, indexer, value, name: str):

~/sources/official.clone/pandas/pandas/core/indexing.py in _setitem_single_block(self, indexer, value, name)
1848             # setting for extensionarrays that store dicts. Need to decide
1849             # if it's worth supporting that.
-> 1850             value = self._align_series(indexer, Series(value))
1851
1852         elif isinstance(value, ABCDataFrame) and name != "iloc":

~/sources/official.clone/pandas/pandas/core/indexing.py in _align_series(self, indexer, ser, multiindex_indexer)
2018
2019         elif is_scalar(indexer):
-> 2020             ax = self.obj._get_axis(1)
2021
2022             if ser.index.equals(ax):

~/sources/official.clone/pandas/pandas/core/generic.py in _get_axis(self, axis)
467     @final
468     def _get_axis(self, axis: Axis) -> Index:
--> 469         axis_number = self._get_axis_number(axis)
470         assert axis_number in {0, 1}
471         return self.index if axis_number == 0 else self.columns

~/sources/official.clone/pandas/pandas/core/generic.py in _get_axis_number(cls, axis)
457             return cls._AXIS_TO_AXIS_NUMBER[axis]
458         except KeyError:
--> 459             raise ValueError(f"No axis named {axis} for object type {cls.__name__}")
460
461     @final

ValueError: No axis named 1 for object type Series

# Fixed code
def _align_series(self, indexer, ser: Series, multiindex_indexer: bool = False):
    """
    Parameters
    ----------
    indexer : tuple, slice, scalar
        Indexer used to get the locations that will be set to `ser`.
    ser : pd.Series
        Values to assign to the locations specified by `indexer`.
    multiindex_indexer : boolean, optional
        Defaults to False. Should be set to True if `indexer` was from
        a `pd.MultiIndex`, to avoid unnecessary broadcasting.

    Returns
    -------
    `np.array` of `ser` broadcast to the appropriate shape for assignment
    to the locations selected by `indexer`
    """
    if isinstance(indexer, (slice, np.ndarray, list, Index)):
        indexer = (indexer,)

    if isinstance(indexer, tuple):
        # flatten np.ndarray indexers
        def ravel(i):
            return i.ravel() if isinstance(i, np.ndarray) else i

        indexer = tuple(map(ravel, indexer))

        aligners = [not com.is_null_slice(idx) for idx in indexer]
        sum_aligners = sum(aligners)
        single_aligner = sum_aligners == 1
        is_frame = self.ndim == 2
        obj = self.obj

        # are we a single alignable value on a non-primary
        # dim (e.g. panel: 1,2, or frame: 0) ?
        # hence need to align to a single axis dimension
        # rather that find all valid dims

        # frame
        if is_frame:
            single_aligner = single_aligner and aligners[0]

        # we have a frame, with multiple indexers on both axes; and a
        # series, so need to broadcast (see GH5206)
        if sum_aligners == self.ndim and all(is_sequence(_) for _ in indexer):
            ser = ser.reindex(obj.axes[0][indexer[0]], copy=True)._values

            # single indexer
            if len(indexer) > 1 and not multiindex_indexer:
                len_indexer = len(indexer[1])
                ser = np.tile(ser, len_indexer).reshape(len_indexer, -1).T

            return ser

        for i, idx in enumerate(indexer):
            ax = obj.axes[i]

            # multiple aligners (or null slices)
            if is_sequence(idx) or isinstance(idx, slice):
                if single_aligner and com.is_null_slice(idx):
                    continue
                new_ix = ax[idx]
                if not is_list_like_indexer(new_ix):
                    new_ix = Index([new_ix])
                else:
                    new_ix = Index(new_ix)
                if ser.index.equals(new_ix) or not len(new_ix):
                    return ser._values.copy()

                return ser.reindex(new_ix)._values

            # 2 dims
            elif single_aligner:
                # reindex along index
                ax = self.obj.axes[1]
                if ser.index.equals(ax) or not len(ax):
                    return ser._values.copy()
                return ser.reindex(ax)._values

    elif is_integer(indexer) and self.ndim == 1:
        if is_object_dtype(self.obj):
            return ser
        ax = self.obj._get_axis(0)

        if ser.index.equals(ax):
            return ser._values.copy()

        return ser.reindex(ax)._values[indexer]

    elif is_integer(indexer):
        ax = self.obj._get_axis(1)

        if ser.index.equals(ax):
            return ser._values.copy()

        return ser.reindex(ax)._values

    raise ValueError("Incompatible indexer with Series")