FoundationAgents/OpenManus · error · RuntimeError

Maximum number of sandboxes ({self.max_sandboxes}) reached

Error message

Maximum number of sandboxes ({self.max_sandboxes}) reached

What it means

Raised by SandboxManager.create_sandbox (app/sandbox/core/manager.py:133) while holding _global_lock when len(self._sandboxes) >= self.max_sandboxes. It is a hard capacity cap: the manager refuses to allocate a new Docker sandbox until an existing one is deleted. Because the check happens under the global lock, concurrent creators serialize and the first one past the cap fails.

Source

Thrown at app/sandbox/core/manager.py:133

        self,
        config: Optional[SandboxSettings] = None,
        volume_bindings: Optional[Dict[str, str]] = None,
    ) -> str:
        """Creates a new sandbox instance.

        Args:
            config: Sandbox configuration.
            volume_bindings: Volume mapping configuration.

        Returns:
            str: Sandbox ID.

        Raises:
            RuntimeError: If max sandbox count reached or creation fails.
        """
        async with self._global_lock:
            if len(self._sandboxes) >= self.max_sandboxes:
                raise RuntimeError(
                    f"Maximum number of sandboxes ({self.max_sandboxes}) reached"
                )

            config = config or SandboxSettings()
            if not await self.ensure_image(config.image):
                raise RuntimeError(f"Failed to ensure Docker image: {config.image}")

            sandbox_id = str(uuid.uuid4())
            try:
                sandbox = DockerSandbox(config, volume_bindings)
                await sandbox.create()

                self._sandboxes[sandbox_id] = sandbox
                self._last_used[sandbox_id] = asyncio.get_event_loop().time()
                self._locks[sandbox_id] = asyncio.Lock()

                logger.info(f"Created sandbox {sandbox_id}")
                return sandbox_id

View on GitHub (pinned to 52a13f2a57)

Solutions

  1. Delete sandboxes you no longer need: await manager.delete_sandbox(sandbox_id) in a finally block.
  2. Reuse sandboxes from the pool (get an existing idle one) instead of creating a new one per task.
  3. Increase max_sandboxes in the manager configuration if host resources allow.
  4. Add a periodic reaper that deletes sandboxes idle since _last_used beyond a threshold.

Example fix

# before
sid = await manager.create_sandbox(config)
result = await run_task(sid)  # sandbox leaked on exception

# after
sid = await manager.create_sandbox(config)
try:
    result = await run_task(sid)
finally:
    await manager.delete_sandbox(sid)
Defensive patterns

Strategy: validation

Validate before calling

if manager.active_count() >= manager.max_sandboxes:
    sid = await manager.get_idle_sandbox() or await manager.reap_idle_sandbox()
    # reuse or free before creating
sid = await manager.create_sandbox(config)

Try / catch

try:
    sid = await manager.create_sandbox(config)
except RuntimeError as e:
    if "Maximum number of sandboxes" in str(e):
        await manager.delete_sandbox(oldest_active_id())
        sid = await manager.create_sandbox(config)
    else:
        raise

Prevention

When it happens

Trigger: Creating sandboxes in a loop (one per task/request) without calling delete_sandbox; leaking sandboxes after exceptions so _sandboxes only grows; running concurrent agent sessions whose combined sandbox count hits max_sandboxes.

Common situations: No reaping of idle sandboxes in a long-lived service; a request handler that allocates a sandbox per request but only frees it on the happy path; miscounting because delete_sandbox was never awaited after an error.

Related errors


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