# Buggy code
def _unpack_observation(self, obs_batch):
    """Unpacks the observation, action mask, and state (if present)
    from agent grouping.

    Returns:
        obs (np.ndarray): obs tensor of shape [B, n_agents, obs_size]
        mask (np.ndarray): action mask, if any
        state (np.ndarray or None): state tensor of shape [B, state_size]
            or None if it is not in the batch
    """
    unpacked = _unpack_obs(
        np.array(obs_batch, dtype=np.float32),
        self.observation_space.original_space,
        tensorlib=np,
    )
    if self.has_action_mask:
        obs = np.concatenate([o["obs"] for o in unpacked], axis=1).reshape(
            [len(obs_batch), self.n_agents, self.obs_size]
        )
        action_mask = np.concatenate(
            [o["action_mask"] for o in unpacked], axis=1
        ).reshape([len(obs_batch), self.n_agents, self.n_actions])
    else:
        if isinstance(unpacked[0], dict):
            unpacked_obs = [u["obs"] for u in unpacked]
        else:
            unpacked_obs = unpacked
        obs = np.concatenate(unpacked_obs, axis=1).reshape(
            [len(obs_batch), self.n_agents, self.obs_size]
        )
        action_mask = np.ones(
            [len(obs_batch), self.n_agents, self.n_actions], dtype=np.float32
        )

    if self.has_env_global_state:
        state = unpacked[0][ENV_STATE]
    else:
        state = None
    return obs, action_mask, state


# Traceback (ValueError)
2020-05-20 15:40:03,477 ERROR trial_runner.py:519 -- Trial QMIX_grouped_twostep_c960c_00002: Error processing event.
Traceback (most recent call last):
File "[...]/ray/tune/trial_runner.py", line 467, in _process_trial
result = self.trial_executor.fetch_result(trial)
File "[...]/ray/tune/ray_trial_executor.py", line 430, in fetch_result
result = ray.get(trial_future[0], DEFAULT_GET_TIMEOUT)
File "[...]/ray/worker.py", line 1516, in get
raise value.as_instanceof_cause()
ray.exceptions.RayTaskError(ValueError): ray::QMIX.train() (pid=54834, ip=128.232.69.20)
File "python/ray/_raylet.pyx", line 460, in ray._raylet.execute_task
File "python/ray/_raylet.pyx", line 414, in ray._raylet.execute_task.function_executor
File "[...]/ray/rllib/agents/trainer.py", line 504, in train
raise e
File "[...]/ray/rllib/agents/trainer.py", line 490, in train
result = Trainable.train(self)
File "[...]/ray/tune/trainable.py", line 260, in train
result = self._train()
File "[...]/ray/rllib/agents/trainer_template.py", line 138, in _train
return self._train_exec_impl()
File "[...]/ray/rllib/agents/trainer_template.py", line 173, in _train_exec_impl
res = next(self.train_exec_impl)
File "[...]/ray/util/iter.py", line 689, in __next__
return next(self.built_iterator)
File "[...]/ray/util/iter.py", line 702, in apply_foreach
for item in it:
File "[...]/ray/util/iter.py", line 772, in apply_filter
for item in it:
File "[...]/ray/util/iter.py", line 772, in apply_filter
for item in it:
File "[...]/ray/util/iter.py", line 702, in apply_foreach
for item in it:
File "[...]/ray/util/iter.py", line 772, in apply_filter
for item in it:
File "[...]/ray/util/iter.py", line 977, in build_union
item = next(it)
File "[...]/ray/util/iter.py", line 689, in __next__
return next(self.built_iterator)
File "[...]/ray/util/iter.py", line 702, in apply_foreach
for item in it:
File "[...]/ray/util/iter.py", line 702, in apply_foreach
for item in it:
File "[...]/ray/util/iter.py", line 702, in apply_foreach
for item in it:
File "[...]/ray/rllib/execution/rollout_ops.py", line 70, in sampler
yield workers.local_worker().sample()
File "[...]/ray/rllib/evaluation/rollout_worker.py", line 515, in sample
batches = [self.input_reader.next()]
File "[...]/ray/rllib/evaluation/sampler.py", line 56, in next
batches = [self.get_data()]
File "[...]/ray/rllib/evaluation/sampler.py", line 101, in get_data
item = next(self.rollout_provider)
File "[...]/ray/rllib/evaluation/sampler.py", line 367, in _env_runner
active_episodes)
File "[...]/ray/rllib/evaluation/sampler.py", line 637, in _do_policy_eval
timestep=policy.global_timestep)
File "[...]/ray/rllib/agents/qmix/qmix_policy.py", line 260, in compute_actions
obs_batch, action_mask, _ = self._unpack_observation(obs_batch)
File "[...]/ray/rllib/agents/qmix/qmix_policy.py", line 486, in _unpack_observation
axis=1).reshape([len(obs_batch), self.n_agents, self.obs_size])
File "<__array_function__ internals>", line 6, in concatenate
ValueError: zero-dimensional arrays cannot be concatenated

# Fixed code
def _unpack_observation(self, obs_batch):
    """Unpacks the observation, action mask, and state (if present)
    from agent grouping.

    Returns:
        obs (np.ndarray): obs tensor of shape [B, n_agents, obs_size]
        mask (np.ndarray): action mask, if any
        state (np.ndarray or None): state tensor of shape [B, state_size]
            or None if it is not in the batch
    """

    unpacked = _unpack_obs(
        np.array(obs_batch, dtype=np.float32),
        self.observation_space.original_space,
        tensorlib=np,
    )

    if isinstance(unpacked[0], dict):
        unpacked_obs = [np.concatenate(tree.flatten(u["obs"]), 1) for u in unpacked]
    else:
        unpacked_obs = unpacked

    obs = np.concatenate(unpacked_obs, axis=1).reshape(
        [len(obs_batch), self.n_agents, self.obs_size]
    )

    if self.has_action_mask:
        action_mask = np.concatenate(
            [o["action_mask"] for o in unpacked], axis=1
        ).reshape([len(obs_batch), self.n_agents, self.n_actions])
    else:
        action_mask = np.ones(
            [len(obs_batch), self.n_agents, self.n_actions], dtype=np.float32
        )

    if self.has_env_global_state:
        state = unpacked[0][ENV_STATE]
    else:
        state = None
    return obs, action_mask, state