FoundationAgents/OpenManus · error · TimeoutError

Command '{cmd}' timed out after {timeout} seconds

Error message

Command '{cmd}' timed out after {timeout} seconds

What it means

LocalFileOperator.run_command runs a subprocess and bounds process.communicate() with asyncio.wait_for(timeout) (default 120s). On expiry it kills the process and raises TimeoutError naming the command and limit. Note the kill is best-effort (ProcessLookupError swallowed) — child processes of a shell=True command may survive the kill.

Source

Thrown at app/tool/file_operators.py:91

        process = await asyncio.create_subprocess_shell(
            cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
        )

        try:
            stdout, stderr = await asyncio.wait_for(
                process.communicate(), timeout=timeout
            )
            return (
                process.returncode or 0,
                stdout.decode(),
                stderr.decode(),
            )
        except asyncio.TimeoutError as exc:
            try:
                process.kill()
            except ProcessLookupError:
                pass
            raise TimeoutError(
                f"Command '{cmd}' timed out after {timeout} seconds"
            ) from exc


class SandboxFileOperator(FileOperator):
    """File operations implementation for sandbox environment."""

    def __init__(self):
        self.sandbox_client = SANDBOX_CLIENT

    async def _ensure_sandbox_initialized(self):
        """Ensure sandbox is initialized."""
        if not self.sandbox_client.sandbox:
            await self.sandbox_client.create(config=SandboxSettings())

    async def read_file(self, path: PathLike) -> str:
        """Read content from a file in sandbox."""
        await self._ensure_sandbox_initialized()

View on GitHub (pinned to 52a13f2a57)

Solutions

  1. Pass an explicit, realistic timeout for slow steps: await op.run_command('npm ci', timeout=600).
  2. Detached long-running processes: append 'nohup ... > /tmp/log 2>&1 &' so the shell returns instantly and the log is read separately.
  3. For pipelines under shell=True, close inherited fds by redirecting every child: 'cmd > /tmp/out.log 2>&1 < /dev/null'.
  4. Catch TimeoutError, then verify the process tree is actually dead (pkill -f pattern) before retrying, since kill() may miss grandchildren.

Example fix

# before
rc, out, err = await op.run_command("python -m http.server 8000")  # never exits -> TimeoutError

# after
rc, out, err = await op.run_command(
    "nohup python -m http.server 8000 > /tmp/http.log 2>&1 < /dev/null &"
)
rc, out, err = await op.run_command("sleep 1 && cat /tmp/http.log")
Defensive patterns

Strategy: try-catch

Validate before calling

rc, out, err = await op.run_command('test -d node_modules && echo yes || echo no')
slow = ('install', 'build', 'test', 'pytest', 'compile')
timeout = 600 if any(s in cmd for s in slow) else 120
rc, out, err = await op.run_command(cmd, timeout=timeout)

Try / catch

try:
    rc, out, err = await op.run_command(cmd, timeout=120)
except TimeoutError:
    await op.run_command('pkill -f "<cmd-pattern>" || true', timeout=10)  # kill survivors
    rc, out, err = await op.run_command(cmd + ' > /tmp/cmd.log 2>&1 < /dev/null', timeout=600)

Prevention

When it happens

Trigger: Any command exceeding the timeout passed (or the 120s default): package installs, test suites, builds; commands started with shell=True that spawn children holding the pipes open, so even a 'finished' parent blocks communicate(); forgetting to pass timeout for a known-slow step.

Common situations: 'npm install' / 'pip install' on cold caches; running a full test suite through the file operator; starting servers in the foreground; shell pipelines where a backgrounded child keeps stdout open.

Understand the failure class

Related errors


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