# Buggy code
def decode_cf_variable(
    name,
    var,
    concat_characters=True,
    mask_and_scale=True,
    decode_times=True,
    decode_endianness=True,
    stack_char_dim=True,
):
    """
    Decodes a variable which may hold CF encoded information.

    This includes variables that have been masked and scaled, which
    hold CF style time variables (this is almost always the case if
    the dataset has been serialized) and which have strings encoded
    as character arrays.

    Parameters
    ----------
    name: str
        Name of the variable. Used for better error messages.
    var : Variable
        A variable holding potentially CF encoded information.
    concat_characters : bool
        Should character arrays be concatenated to strings, for
        example: ['h', 'e', 'l', 'l', 'o'] -> 'hello'
    mask_and_scale: bool
        Lazily scale (using scale_factor and add_offset) and mask
        (using _FillValue). If the _Unsigned attribute is present
        treat integer arrays as unsigned.
    decode_times : bool
        Decode cf times ('hours since 2000-01-01') to np.datetime64.
    decode_endianness : bool
        Decode arrays from non-native to native endianness.
    stack_char_dim : bool
        Whether to stack characters into bytes along the last dimension of this
        array. Passed as an argument because we need to look at the full
        dataset to figure out if this is appropriate.

    Returns
    -------
    out : Variable
        A variable holding the decoded equivalent of var.
    """
    # use _data instead of data so as not to trigger loading data
    var = as_variable(var)
    data = var._data
    dimensions = var.dims
    attributes = var.attrs.copy()
    encoding = var.encoding.copy()

    original_dtype = data.dtype

    if concat_characters and data.dtype.kind == "S":
        if stack_char_dim:
            dimensions = dimensions[:-1]
            data = StackedBytesArray(data)

        string_encoding = pop_to(attributes, encoding, "_Encoding")
        if string_encoding is not None:
            data = BytesToStringArray(data, string_encoding)

    # TODO(shoyer): convert everything above to use coders
    var = Variable(dimensions, data, attributes, encoding)

    if mask_and_scale:
        for coder in [
            variables.UnsignedIntegerCoder(),
            variables.CFMaskCoder(),
            variables.CFScaleOffsetCoder(),
        ]:
            var = coder.decode(var, name=name)
    if decode_times:
        for coder in [times.CFTimedeltaCoder(), times.CFDatetimeCoder()]:
            var = coder.decode(var, name=name)

    dimensions, data, attributes, encoding = variables.unpack_for_decoding(var)
    # TODO(shoyer): convert everything below to use coders

    if decode_endianness and not data.dtype.isnative:
        # do this last, so it's only done if we didn't already unmask/scale
        data = NativeEndiannessArray(data)
        original_dtype = data.dtype

    if "dtype" in encoding:
        if original_dtype != encoding["dtype"]:
            warnings.warn(
                "CF decoding is overwriting dtype on variable {!r}".format(name)
            )
    else:
        encoding["dtype"] = original_dtype

    if "dtype" in attributes and attributes["dtype"] == "bool":
        del attributes["dtype"]
        data = BoolTypeArray(data)

    if not isinstance(data, dask_array_type):
        data = indexing.LazilyOuterIndexedArray(data)
    return Variable(dimensions, data, attributes, encoding=encoding)


# 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 decode_cf_variable(
    name,
    var,
    concat_characters=True,
    mask_and_scale=True,
    decode_times=True,
    decode_endianness=True,
    stack_char_dim=True,
):
    """
    Decodes a variable which may hold CF encoded information.

    This includes variables that have been masked and scaled, which
    hold CF style time variables (this is almost always the case if
    the dataset has been serialized) and which have strings encoded
    as character arrays.

    Parameters
    ----------
    name: str
        Name of the variable. Used for better error messages.
    var : Variable
        A variable holding potentially CF encoded information.
    concat_characters : bool
        Should character arrays be concatenated to strings, for
        example: ['h', 'e', 'l', 'l', 'o'] -> 'hello'
    mask_and_scale: bool
        Lazily scale (using scale_factor and add_offset) and mask
        (using _FillValue). If the _Unsigned attribute is present
        treat integer arrays as unsigned.
    decode_times : bool
        Decode cf times ('hours since 2000-01-01') to np.datetime64.
    decode_endianness : bool
        Decode arrays from non-native to native endianness.
    stack_char_dim : bool
        Whether to stack characters into bytes along the last dimension of this
        array. Passed as an argument because we need to look at the full
        dataset to figure out if this is appropriate.

    Returns
    -------
    out : Variable
        A variable holding the decoded equivalent of var.
    """
    var = as_variable(var)
    original_dtype = var.dtype

    if concat_characters:
        if stack_char_dim:
            var = strings.CharacterArrayCoder().decode(var, name=name)
        var = strings.EncodedStringCoder().decode(var)

    if mask_and_scale:
        for coder in [
            variables.UnsignedIntegerCoder(),
            variables.CFMaskCoder(),
            variables.CFScaleOffsetCoder(),
        ]:
            var = coder.decode(var, name=name)

    if decode_times:
        for coder in [times.CFTimedeltaCoder(), times.CFDatetimeCoder()]:
            var = coder.decode(var, name=name)

    dimensions, data, attributes, encoding = variables.unpack_for_decoding(var)
    # TODO(shoyer): convert everything below to use coders

    if decode_endianness and not data.dtype.isnative:
        # do this last, so it's only done if we didn't already unmask/scale
        data = NativeEndiannessArray(data)
        original_dtype = data.dtype

    if "dtype" in encoding:
        if original_dtype != encoding["dtype"]:
            warnings.warn(
                "CF decoding is overwriting dtype on variable {!r}".format(name)
            )
    else:
        encoding["dtype"] = original_dtype

    if "dtype" in attributes and attributes["dtype"] == "bool":
        del attributes["dtype"]
        data = BoolTypeArray(data)

    if not isinstance(data, dask_array_type):
        data = indexing.LazilyOuterIndexedArray(data)

    return Variable(dimensions, data, attributes, encoding=encoding)