agent.close() and await agent.aclose() shut down every resource the agent owns — LLM clients, memory, MCP tools, runtime MCPs, and per-agent circuit breakers — in one call.
Quick Start
1
Simple
Pass an MCP at construction time;
close() shuts down its subprocess and stream.2
Async
Use the async context manager or call
aclose() explicitly — it prefers each tool’s aclose().How It Works
Closing an agent walks each owned resource and shuts it down best-effort.close() / aclose() set _closed = True and become no-ops on subsequent calls, so calling them twice is safe. Check agent.is_closed to confirm.Before PR #5083 the async path (
aclose()) skipped LLM-client teardown entirely, so a per-request clone in the HTTP invoke API leaked its client pool even when the caller (correctly) awaited aclose(). If you build long-running services that rely on await agent.aclose(), upgrade to that release.Garbage-Collection Safety
An agent that is dropped withoutclose() is cleaned up by a finalizer that never blocks.
- No deadlock.
Agent.__del__reads the approval registry directly, without taking its lock. GC firing on a thread that holds the lock no longer hangs. - Silent no-op without a registry. If no approval-gated tool ever ran, there is nothing to release.
- No leaked approval keys. Agents created and dropped in a long-lived worker thread, event loop, or notebook no longer pile up cached approvals. The next approval write prunes dead agents’ keys.
close() / aclose() and with blocks stay the authoritative teardown. They are deterministic and release resources immediately; GC only runs when Python decides to.Best Practices
Prefer a team-level context manager
Prefer a team-level context manager
Wrap a multi-agent run in
with PraisonAIAgents(...) as workflow: — the team-level context manager fans out to each agent’s close(). See Resource Lifecycle.Use try/finally for a single agent
Use try/finally for a single agent
For a lone agent, wrap the run in
try/finally and call agent.close() (or await agent.aclose()), so cleanup runs even when the run raises.Use aclose() for async teardown of OpenAI clients
Use aclose() for async teardown of OpenAI clients
agent.close() releases the sync HTTP client and drops the cached async client reference; it cannot close the async client (its close() is a coroutine). For deterministic async teardown — a FastAPI lifespan, a worker shutdown hook, or tests run with -W error::ResourceWarning — call await agent.aclose().Don't rely on del alone
Don't rely on del alone
Garbage-collection cleanup is best-effort and never blocks — the async client cannot be awaited from a finalizer. Call
close() / aclose() (or use with) explicitly for deterministic teardown.Cleanup never masks your error
Cleanup never masks your error
Each step is guarded individually; failures are logged as warnings and never raise, so a bad shutdown never hides the original exception.
Long-running invoke servers close clones for you
Long-running invoke servers close clones for you
The HTTP invoke API (
POST /api/v1/agents/{id}/invoke) and invoke_agent_standalone() both wrap the invocation in try/finally and close the per-request clone after every request. You do not need to add extra cleanup around individual invokes; the wrapper handles per-request clones. See n8n API → Per-request cleanup.Long-running servers: approval grants are reclaimed
Long-running servers: approval grants are reclaimed
If you create one
Agent(...) per request or session and never call close(), the process-global approval registry no longer grows without bound — __del__ also releases the agent’s scope when it is garbage-collected. close() / aclose() remain the recommended deterministic path, since finalizer timing is not guaranteed. Only the closing agent’s own grants are dropped; every other live agent keeps its approvals. See Permissions → Approval-scope cleanup.Related
Runtime MCP
Attach and detach MCP servers at runtime
MCP Lifecycle
Per-MCP context manager and manual shutdown
Resource Lifecycle
Team-level cleanup with PraisonAIAgents
Tool Circuit Breaker
How per-agent breakers work and auto-prune
Permissions
Approval scopes and the cleanup that close() performs
n8n API
Per-request clone cleanup in the HTTP invoke endpoint
Agent Invoke Registry
Templates vs. per-request clones and their teardown

