# Buggy code
def ensure_dtype_not_object(var, name=None):
    # TODO: move this from conventions to backends? (it's not CF related)
    if var.dtype.kind == "O":
        if isinstance(var, IndexVariable) and isinstance(var.to_index(), pd.MultiIndex):
            raise NotImplementedError(
                "variable {!r} is a MultiIndex, which cannot yet be "
                "serialized to netCDF files "
                "(https://github.com/pydata/xarray/issues/1077). Use "
                "reset_index() to convert MultiIndex levels into coordinate "
                "variables instead.".format(name)
            )

        dims, data, attrs, encoding = _var_as_tuple(var)
        missing = pd.isnull(data)
        if missing.any():
            # nb. this will fail for dask.array data
            non_missing_values = data[~missing]
            inferred_dtype = _infer_dtype(non_missing_values, name)

            # There is no safe bit-pattern for NA in typical binary string
            # formats, we so can't set a fill_value. Unfortunately, this means
            # we can't distinguish between missing values and empty strings.
            if inferred_dtype.kind == "S":
                fill_value = b""
            elif inferred_dtype.kind == "U":
                fill_value = ""
            else:
                # insist on using float for numeric values
                if not np.issubdtype(inferred_dtype, float):
                    inferred_dtype = np.dtype(float)
                fill_value = inferred_dtype.type(np.nan)

            data = np.array(data, dtype=inferred_dtype, copy=True)
            data[missing] = fill_value
        else:
            data = data.astype(dtype=_infer_dtype(data, name))
        var = Variable(dims, data, attrs, encoding)
    return var


# Traceback (UnboundLocalError)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-26-2052b95f2d95> in <module>()
----> 1 no2 = xr.open_dataset(product, group='/PRODUCT')

/Users/stefano/anaconda/lib/python3.6/site-packages/xarray/backends/api.py in open_dataset(filename_or_obj, group, decode_cf, mask_and_scale, decode_times, autoclose, concat_characters, decode_coords, engine, chunks, lock, cache, drop_variables)
303             lock = _default_lock(filename_or_obj, engine)
304         with close_on_error(store):
--> 305             return maybe_decode_store(store, lock)
306     else:
307         if engine is not None and engine != 'scipy':

/Users/stefano/anaconda/lib/python3.6/site-packages/xarray/backends/api.py in maybe_decode_store(store, lock)
223             store, mask_and_scale=mask_and_scale, decode_times=decode_times,
224             concat_characters=concat_characters, decode_coords=decode_coords,
--> 225             drop_variables=drop_variables)
226
227         _protect_dataset_variables_inplace(ds, cache)

/Users/stefano/anaconda/lib/python3.6/site-packages/xarray/conventions.py in decode_cf(obj, concat_characters, mask_and_scale, decode_times, decode_coords, drop_variables)
1153     vars, attrs, coord_names = decode_cf_variables(
1154         vars, attrs, concat_characters, mask_and_scale, decode_times,
-> 1155         decode_coords, drop_variables=drop_variables)
1156     ds = Dataset(vars, attrs=attrs)
1157     ds = ds.set_coords(coord_names.union(extra_coords).intersection(vars))

/Users/stefano/anaconda/lib/python3.6/site-packages/xarray/conventions.py in decode_cf_variables(variables, attributes, concat_characters, mask_and_scale, decode_times, decode_coords, drop_variables)
1086             k, v, concat_characters=concat_characters,
1087             mask_and_scale=mask_and_scale, decode_times=decode_times,
-> 1088             stack_char_dim=stack_char_dim)
1089         if decode_coords:
1090             var_attrs = new_vars[k].attrs

/Users/stefano/anaconda/lib/python3.6/site-packages/xarray/conventions.py in decode_cf_variable(name, var, concat_characters, mask_and_scale, decode_times, decode_endianness, stack_char_dim)
998         if (has_fill or scale_factor is not None or add_offset is not None):
999             if has_fill and np.array(fill_value).dtype.kind in ['U', 'S', 'O']:
-> 1000                 if string_encoding is not None:
1001                     raise NotImplementedError(
1002                         'variable %r has a _FillValue specified, but '

UnboundLocalError: local variable 'string_encoding' referenced before assignment

# Fixed code
def ensure_dtype_not_object(var, name=None):
    # TODO: move this from conventions to backends? (it's not CF related)
    if var.dtype.kind == "O":
        dims, data, attrs, encoding = _var_as_tuple(var)
        missing = pd.isnull(data)
        if missing.any():
            # nb. this will fail for dask.array data
            non_missing_values = data[~missing]
            inferred_dtype = _infer_dtype(non_missing_values, name)

            # There is no safe bit-pattern for NA in typical binary string
            # formats, we so can't set a fill_value. Unfortunately, this means
            # we can't distinguish between missing values and empty strings.
            if inferred_dtype.kind == "S":
                fill_value = b""
            elif inferred_dtype.kind == "U":
                fill_value = ""
            else:
                # insist on using float for numeric values
                if not np.issubdtype(inferred_dtype, float):
                    inferred_dtype = np.dtype(float)
                fill_value = inferred_dtype.type(np.nan)

            data = np.array(data, dtype=inferred_dtype, copy=True)
            data[missing] = fill_value
        else:
            data = data.astype(dtype=_infer_dtype(data, name))
        var = Variable(dims, data, attrs, encoding)
    return var