{"record":{"id":"3520e85a518e7a25","repo":"Unity-Technologies/ml-agents","slug":"agent-id-agent-id-is-not-present-in-the-terminal","errorCode":null,"errorMessage":"agent_id {agent_id} is not present in the TerminalSteps","messagePattern":"agent_id (.+?) is not present in the TerminalSteps","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"ml-agents-envs/mlagents_envs/base_env.py","lineNumber":230,"sourceCode":"        \"\"\"\n        if self._agent_id_to_index is None:\n            self._agent_id_to_index = {}\n            for a_idx, a_id in enumerate(self.agent_id):\n                self._agent_id_to_index[a_id] = a_idx\n        return self._agent_id_to_index\n\n    def __len__(self) -> int:\n        return len(self.agent_id)\n\n    def __getitem__(self, agent_id: AgentId) -> TerminalStep:\n        \"\"\"\n        returns the TerminalStep for a specific agent.\n        :param agent_id: The id of the agent\n        :returns: obs, reward, done, agent_id and optional action mask for a\n        specific agent\n        \"\"\"\n        if agent_id not in self.agent_id_to_index:\n            raise KeyError(f\"agent_id {agent_id} is not present in the TerminalSteps\")\n        agent_index = self._agent_id_to_index[agent_id]  # type: ignore\n        agent_obs = []\n        for batched_obs in self.obs:\n            agent_obs.append(batched_obs[agent_index])\n        group_id = self.group_id[agent_index]\n        return TerminalStep(\n            obs=agent_obs,\n            reward=self.reward[agent_index],\n            interrupted=self.interrupted[agent_index],\n            agent_id=agent_id,\n            group_id=group_id,\n            group_reward=self.group_reward[agent_index],\n        )\n\n    def __iter__(self) -> Iterator[Any]:\n        yield from self.agent_id\n\n    @staticmethod","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/Unity-Technologies/ml-agents/blob/3ecb446f75d1e7400eb404c562dc005d3164cffc/ml-agents-envs/mlagents_envs/base_env.py#L212-L248","documentation":"TerminalSteps.__getitem__ raises KeyError when the requested agent_id is not among the agents that terminated this step. Like DecisionSteps, only ids present in the current terminal batch are valid keys in agent_id_to_index. The library raises this rather than returning a default so callers don't silently treat non-terminated agents as done.","triggerScenarios":"Indexing terminal_steps[agent_id] where that agent did not end its episode on the current step — e.g. the agent is still active and appears in DecisionSteps instead.","commonSituations":"Unconditionally reading terminal_steps[agent_id] for every agent every step even though only a few terminate per step; assuming an agent finished when it merely produced a decision; ids that never existed (typo, stale id).","solutions":["Guard with `if agent_id in terminal_steps:` before indexing.","Only iterate terminal_steps.agent_id (the ids that actually finished) rather than all known ids.","Treat absence from TerminalSteps as 'agent still active' and read its data from DecisionSteps."],"exampleFix":"# before\nobs, reward, done, id = terminal_steps[agent_id]  # KeyError: agent still active\n# after\nif agent_id in terminal_steps:\n    obs, reward, done, id = terminal_steps[agent_id]\nelse:\n    active = decision_steps[agent_id]  # still running","handlingStrategy":"type-guard","validationCode":"if agent_id not in terminal_steps.agent_id_to_index:\n    # agent did not terminate this step — still active","typeGuard":"def is_terminal(ts, agent_id: int) -> bool:\n    return agent_id in ts.agent_id_to_index","tryCatchPattern":"try:\n    result = terminal_steps[agent_id]\nexcept KeyError:\n    result = None  # not done yet","preventionTips":["Only read terminal data for ids in terminal_steps.agent_id","Treat absence from TerminalSteps as active and use DecisionSteps","Track episode state per agent id instead of unconditional indexing"],"tags":["python","ml-agents","keyerror","terminal-steps","agent-lifecycle"],"backgroundTag":"key-not-found","analyzedSha":"3ecb446f75d1e7400eb404c562dc005d3164cffc","analyzedAt":"2026-09-02T16:33:12.832Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T21:17:11.164Z"}