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_idView on GitHub (pinned to 52a13f2a57)
Solutions
- Delete sandboxes you no longer need: await manager.delete_sandbox(sandbox_id) in a finally block.
- Reuse sandboxes from the pool (get an existing idle one) instead of creating a new one per task.
- Increase max_sandboxes in the manager configuration if host resources allow.
- 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
- Always pair create_sandbox with delete_sandbox in try/finally
- Run a periodic reaper keyed on manager._last_used
- Keep max_sandboxes below host memory / disk capacity
- Pool and reuse sandboxes per task type instead of one per request
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
- Failed to create sandbox: {e}
- Failed to create sandbox: {e}
- Empty tar archive
- password must be provided
- Request may exceed input token limit (Current: {self.total_i
AI-assisted analysis of FoundationAgents/OpenManus@52a13f2a57 (2026-08-15).
Data as JSON: /api/errors/a785ea85363caa73.
Report an issue: GitHub.