FoundationAgents/OpenManus · error · ToolError

timed out: bash has not returned in {self._timeout} seconds

Error message

timed out: bash has not returned in {self._timeout} seconds and must be restarted

What it means

run() refuses new commands once _timed_out is set: a previous command exceeded _timeout, and the session no longer trusts its output stream (the sentinel line from the hung command may still arrive later and corrupt the next read). The message says explicitly the session 'must be restarted' — recovery is a restart, not a retry.

Source

Thrown at app/tool/bash.py:65

    def stop(self):
        """Terminate the bash shell."""
        if not self._started:
            raise ToolError("Session has not started.")
        if self._process.returncode is not None:
            return
        self._process.terminate()

    async def run(self, command: str):
        """Execute a command in the bash shell."""
        if not self._started:
            raise ToolError("Session has not started.")
        if self._process.returncode is not None:
            return CLIResult(
                system="tool must be restarted",
                error=f"bash has exited with returncode {self._process.returncode}",
            )
        if self._timed_out:
            raise ToolError(
                f"timed out: bash has not returned in {self._timeout} seconds and must be restarted",
            )

        # we know these are not None because we created the process with PIPEs
        assert self._process.stdin
        assert self._process.stdout
        assert self._process.stderr

        # send command to the process
        self._process.stdin.write(
            command.encode() + f"; echo '{self._sentinel}'\n".encode()
        )
        await self._process.stdin.drain()

        # read output from the process, until the sentinel is found
        try:
            async with asyncio.timeout(self._timeout):
                while True:

View on GitHub (pinned to 52a13f2a57)

Solutions

  1. After a timeout ToolError, restart the session before the next command: create a new _BashSession, or with the outer Bash tool call execute("restart").
  2. Catch this exact ToolError separately from timeouts: 'timed out' in str(e) → recycle session, then re-issue or skip the command.
  3. Prevent the original timeout (see error 53): larger _timeout, or avoid commands that never print the sentinel.

Example fix

# before
try:
    out = await bash.execute(cmd)
except ToolError:
    out = await bash.execute(next_cmd)  # ToolError: must be restarted

# after
try:
    out = await bash.execute(cmd)
except ToolError as e:
    if "timed out" in str(e):
        out = await bash.execute("restart")   # fresh session
    out = await bash.execute(next_cmd)
Defensive patterns

Strategy: fallback

Validate before calling

if session._timed_out:
    session = _BashSession()
    await session.start()  # or: await bash.execute('restart')

Type guard

def needs_restart(s: _BashSession) -> bool:
    return bool(s._timed_out)

Try / catch

try:
    out = await bash.execute(cmd)
except ToolError as e:
    if 'must be restarted' in str(e):
        await bash.execute('restart')       # fallback: fresh session
        out = await bash.execute(cmd)
    else:
        raise

Prevention

When it happens

Trigger: Command A times out (error 53 fires, _timed_out = True); command B is then sent to the same session and is rejected here before anything is written to the shell.

Common situations: Agent loops that keep issuing commands after catching a ToolError timeout; timeout handlers that log but do not recycle the session.

Understand the failure class

Related errors


AI-assisted analysis of FoundationAgents/OpenManus@52a13f2a57 (2026-08-15). Data as JSON: /api/errors/e0c5d19253c55f3f. Report an issue: GitHub.