# 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":
        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, np.floating):
                    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 (ValueError)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-fff1cd753e7c> in <module>()
2 fs = s3fs.S3FileSystem(anon=False)
3 d = s3fs.S3Map(f_zarr, s3=fs)
----> 4 xr.open_zarr(d)

/opt/conda/lib/python3.6/site-packages/xarray/backends/zarr.py in open_zarr(store, group, synchronizer, auto_chunk, decode_cf, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables)
476
477         variables = OrderedDict([(k, maybe_chunk(k, v))
--> 478                                  for k, v in ds.variables.items()])
479         return ds._replace_vars_and_dims(variables)
480     else:

/opt/conda/lib/python3.6/site-packages/xarray/backends/zarr.py in <listcomp>(.0)
476
477         variables = OrderedDict([(k, maybe_chunk(k, v))
--> 478                                  for k, v in ds.variables.items()])
479         return ds._replace_vars_and_dims(variables)
480     else:

/opt/conda/lib/python3.6/site-packages/xarray/backends/zarr.py in maybe_chunk(name, var)
471                 token2 = tokenize(name, var._data)
472                 name2 = 'zarr-%s' % token2
--> 473                 return var.chunk(chunks, name=name2, lock=None)
474             else:
475                 return var

/opt/conda/lib/python3.6/site-packages/xarray/core/variable.py in chunk(self, chunks, name, lock)
820             data = indexing.ImplicitToExplicitIndexingAdapter(
821                 data, indexing.OuterIndexer)
--> 822             data = da.from_array(data, chunks, name=name, lock=lock)
823
824         return type(self)(self.dims, data, self._attrs, self._encoding,

/opt/conda/lib/python3.6/site-packages/dask/array/core.py in from_array(x, chunks, name, lock, asarray, fancy, getitem)
1988     >>> a = da.from_array(x, chunks=(1000, 1000), lock=True)  # doctest: +SKIP
1989     """
-> 1990     chunks = normalize_chunks(chunks, x.shape)
1991     if name in (None, True):
1992         token = tokenize(x, chunks)

/opt/conda/lib/python3.6/site-packages/dask/array/core.py in normalize_chunks(chunks, shape)
1918             raise ValueError(
1919                 "Chunks and shape must be of the same length/dimension. "
-> 1920                 "Got chunks=%s, shape=%s" % (chunks, shape))
1921
1922     if shape is not None:

ValueError: Chunks and shape must be of the same length/dimension. Got chunks=(3, 64), shape=(3,)

# 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)

        if isinstance(data, dask_array_type):
            warnings.warn(
                "variable {} has data in the form of a dask array with "
                "dtype=object, which means it is being loaded into memory "
                "to determine a data type that can be safely stored on disk. "
                "To avoid this, coerce this variable to a fixed-size dtype "
                "with astype() before saving it.".format(name),
                SerializationWarning,
            )
            data = data.compute()

        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 strings.is_bytes_dtype(inferred_dtype):
                fill_value = b""
            elif strings.is_unicode_dtype(inferred_dtype):
                fill_value = ""
            else:
                # insist on using float for numeric values
                if not np.issubdtype(inferred_dtype, np.floating):
                    inferred_dtype = np.dtype(float)
                fill_value = inferred_dtype.type(np.nan)

            data = _copy_with_dtype(data, dtype=inferred_dtype)
            data[missing] = fill_value
        else:
            data = _copy_with_dtype(data, dtype=_infer_dtype(data, name))

        assert data.dtype.kind != "O" or data.dtype.metadata
        var = Variable(dims, data, attrs, encoding)
    return var