# Buggy code
def fillna(self, value=None, method=None, limit=None):
    """Fill NA/NaN values using the specified method.

    Parameters
    ----------
    method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
        Method to use for filling holes in reindexed Series
        pad / ffill: propagate last valid observation forward to next valid
        backfill / bfill: use NEXT valid observation to fill gap
    value : scalar
        Value to use to fill holes (e.g. 0)
    limit : int, default None
        (Not implemented yet for Categorical!)
        If method is specified, this is the maximum number of consecutive
        NaN values to forward/backward fill. In other words, if there is
        a gap with more than this number of consecutive NaNs, it will only
        be partially filled. If method is not specified, this is the
        maximum number of entries along the entire axis where NaNs will be
        filled.

    Returns
    -------
    filled : Categorical with NA/NaN filled
    """

    if value is None:
        value = np.nan
    if limit is not None:
        raise NotImplementedError(
            "specifying a limit for fillna has not been implemented yet"
        )

    values = self._codes

    # Make sure that we also get NA in categories
    if self.categories.dtype.kind in ["S", "O", "f"]:
        if np.nan in self.categories:
            values = values.copy()
            nan_pos = np.where(isnull(self.categories))[0]
            # we only have one NA in categories
            values[values == nan_pos] = -1

    # pad / bfill
    if method is not None:
        values = self.to_dense().reshape(-1, len(self))
        values = interpolate_2d(values, method, 0, None, value).astype(
            self.categories.dtype
        )[0]
        values = _get_codes_for_values(values, self.categories)

    else:
        if not isnull(value) and value not in self.categories:
            raise ValueError("fill value must be in categories")

        mask = values == -1
        if mask.any():
            values = values.copy()
            values[mask] = self.categories.get_loc(value)

    return self._constructor(
        values, categories=self.categories, ordered=self.ordered, fastpath=True
    )


# Traceback (KeyError)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/indexes/base.py in get_loc(self, key, method, tolerance)
1875             try:
-> 1876                 return self._engine.get_loc(key)
1877             except KeyError:

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:4027)()

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:3891)()

pandas/hashtable.pyx in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:12408)()

pandas/hashtable.pyx in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:12359)()

KeyError: <class 'object'>

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-127-6817ed5716b0> in <module>()
2 tst = pd.DataFrame({'a':[1,2,1,np.nan],
3                    'b':[np.nan, np.nan, np.nan, np.nan]}, dtype='category')
----> 4 tst.fillna(value=tst.median())

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/frame.py in fillna(self, value, method, axis, inplace, limit, downcast, **kwargs)
2754                      self).fillna(value=value, method=method, axis=axis,
2755                                   inplace=inplace, limit=limit,
-> 2756                                   downcast=downcast, **kwargs)
2757
2758     @Appender(_shared_docs['shift'] % _shared_doc_kwargs)

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/generic.py in fillna(self, value, method, axis, inplace, limit, downcast)
3164                         continue
3165                     obj = result[k]
-> 3166                     obj.fillna(v, limit=limit, inplace=True)
3167                 return result
3168             elif not com.is_list_like(value):

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/series.py in fillna(self, value, method, axis, inplace, limit, downcast, **kwargs)
2350                                           axis=axis, inplace=inplace,
2351                                           limit=limit, downcast=downcast,
-> 2352                                           **kwargs)
2353
2354     @Appender(generic._shared_docs['shift'] % _shared_doc_kwargs)

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/generic.py in fillna(self, value, method, axis, inplace, limit, downcast)
3151                 new_data = self._data.fillna(value=value, limit=limit,
3152                                              inplace=inplace,
-> 3153                                              downcast=downcast)
3154
3155             elif isinstance(value, (dict, com.ABCSeries)):

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/internals.py in fillna(self, **kwargs)
2865
2866     def fillna(self, **kwargs):
-> 2867         return self.apply('fillna', **kwargs)
2868
2869     def downcast(self, **kwargs):

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/internals.py in apply(self, f, axes, filter, do_integrity_check, consolidate, **kwargs)
2830
2831             kwargs['mgr'] = self
-> 2832             applied = getattr(b, f)(**kwargs)
2833             result_blocks = _extend_blocks(applied, result_blocks)
2834

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/internals.py in fillna(self, value, limit, inplace, downcast, mgr)
1884         values = self.values if inplace else self.values.copy()
1885         values = self._try_coerce_result(values.fillna(value=value,
-> 1886                                                        limit=limit))
1887         return [self.make_block(values=values)]
1888

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/util/decorators.py in wrapper(*args, **kwargs)
89                 else:
90                     kwargs[new_arg_name] = new_arg_value
---> 91             return func(*args, **kwargs)
92         return wrapper
93     return _deprecate_kwarg

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/core/categorical.py in fillna(self, value, method, limit)
1415             if mask.any():
1416                 values = values.copy()
-> 1417                 values[mask] = self.categories.get_loc(value)
1418
1419         return Categorical(values, categories=self.categories,

/home/dan/.local/opt/miniconda3/envs/mathbs/lib/python3.5/site-packages/pandas/indexes/base.py in get_loc(self, key, method, tolerance)
1876                 return self._engine.get_loc(key)
1877             except KeyError:
-> 1878                 return self._engine.get_loc(self._maybe_cast_indexer(key))
1879
1880         indexer = self.get_indexer([key], method=method, tolerance=tolerance)

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:4027)()

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:3891)()

pandas/hashtable.pyx in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:12408)()

pandas/hashtable.pyx in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:12359)()

KeyError: <class 'object'>

# Fixed code
def fillna(self, value=None, method=None, limit=None):
    """Fill NA/NaN values using the specified method.

    Parameters
    ----------
    method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
        Method to use for filling holes in reindexed Series
        pad / ffill: propagate last valid observation forward to next valid
        backfill / bfill: use NEXT valid observation to fill gap
    value : scalar
        Value to use to fill holes (e.g. 0)
    limit : int, default None
        (Not implemented yet for Categorical!)
        If method is specified, this is the maximum number of consecutive
        NaN values to forward/backward fill. In other words, if there is
        a gap with more than this number of consecutive NaNs, it will only
        be partially filled. If method is not specified, this is the
        maximum number of entries along the entire axis where NaNs will be
        filled.

    Returns
    -------
    filled : Categorical with NA/NaN filled
    """

    if value is None:
        value = np.nan
    if limit is not None:
        raise NotImplementedError(
            "specifying a limit for fillna has not been implemented yet"
        )

    values = self._codes

    # Make sure that we also get NA in categories
    if self.categories.dtype.kind in ["S", "O", "f"]:
        if np.nan in self.categories:
            values = values.copy()
            nan_pos = np.where(isnull(self.categories))[0]
            # we only have one NA in categories
            values[values == nan_pos] = -1

    # pad / bfill
    if method is not None:
        values = self.to_dense().reshape(-1, len(self))
        values = interpolate_2d(values, method, 0, None, value).astype(
            self.categories.dtype
        )[0]
        values = _get_codes_for_values(values, self.categories)

    else:
        if not isnull(value) and value not in self.categories:
            raise ValueError("fill value must be in categories")

        mask = values == -1
        if mask.any():
            values = values.copy()
            if isnull(value):
                values[mask] = -1
            else:
                values[mask] = self.categories.get_loc(value)

    return self._constructor(
        values, categories=self.categories, ordered=self.ordered, fastpath=True
    )