pydantic/monty · error · Error
the pool is closed — create a new Monty pool
Error message
the pool is closed — create a new Monty pool
What it means
Thrown by `Monty.checkout()` when `pool.close()` (or an equivalent teardown, e.g. `await using` disposal) has already ended the pool's native worker set. The pool object still exists but no longer accepts checkout requests; any subsequent session request cannot be served. The fix is to create a fresh pool via `Monty.create()` — the error is terminal for that pool instance, not transient.
Source
Thrown at crates/monty-js/ts/pool.ts:153
...(options.requestTimeout !== undefined ? { requestTimeoutMs: options.requestTimeout * 1000 } : {}),
// `null` disables the backstop; omitted means the 1s default
...(options.durationLimitGrace !== null
? { durationLimitGraceMs: (options.durationLimitGrace ?? 1) * 1000 }
: {}),
...(options.maxCheckoutsPerWorker !== undefined ? { maxCheckoutsPerWorker: options.maxCheckoutsPerWorker } : {}),
})
await native.start()
return new Monty(native)
}
/**
* Checks a worker out of the pool (spawning one if allowed) and creates a
* REPL session in it. Release the worker with `session.close()` (or
* `await using`).
*/
async checkout(options: CheckoutOptions = {}): Promise<MontySession> {
if (this.closed) {
throw new Error('the pool is closed — create a new Monty pool')
}
const assertAnnotations = encodeAssertMessageAnnotations(options.assertMessageAnnotations)
const native = this.native.checkout({
scriptName: options.scriptName ?? 'main.py',
...(options.limits !== undefined ? { limits: options.limits } : {}),
typeCheck: options.typeCheck ?? false,
...(options.typeCheckStubs !== undefined ? { typeCheckStubs: options.typeCheckStubs } : {}),
...(options.typeCheckFormat !== undefined ? { typeCheckFormat: options.typeCheckFormat } : {}),
...(options.typeCheckColor !== undefined ? { typeCheckColor: options.typeCheckColor } : {}),
...(assertAnnotations !== undefined ? { assertMessageAnnotations: assertAnnotations } : {}),
...(options.printFlushInterval !== undefined ? { printFlushIntervalMs: options.printFlushInterval * 1000 } : {}),
})
const telemetryContext = captureTelemetryContext()
await native.enter(telemetryContext)
return new MontySession(native)
}
/**View on GitHub (pinned to adc986b362)
Solutions
- Create a new pool with `Monty.create(...)` before checking out
- Reorder code so all checkouts happen inside the pool's `await using` block
- Store a `closed`/disposed flag alongside the pool and gate callers on it
Example fix
// before await using pool = await Monty.create() await pool.checkout() // after block exits, pool is closed: await pool.checkout() // throws // after const pool = await Monty.create() // plain create, explicit close when done const session = await pool.checkout()
Defensive patterns
Strategy: try-catch
Validate before calling
if (pool.isClosed /* or track disposal yourself */) {
pool = await Monty.create(poolOptions)
} Type guard
function isUsablePool<T extends { closed: boolean }>(pool: T): boolean {
return !pool.closed
} Try / catch
try {
session = await pool.checkout(opts)
} catch (e) {
if (e instanceof Error && e.message.includes('the pool is closed')) {
pool = await Monty.create(poolOptions)
session = await pool.checkout(opts)
} else throw e
} Prevention
- Keep all checkouts inside the `await using pool` scope
- Never stash pools in globals or long-lived closures that outlive disposal
- Wrap pool lifetime in a manager that recreates the pool on demand
When it happens
Trigger: Calling `checkout` after `await pool.close()`, after an `await using pool` block exits, or retaining a pool reference (e.g. in a module-level variable or closure) past its disposal and calling checkout later.
Common situations: Lifetimes: a long-lived singleton pool disposed on shutdown, a callback firing after the `await using` scope ended, retry logic reusing a pool created inside a request handler that already returned.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- loadSession / loadSnapshot is only valid on a fresh session,
- the session is closed — check out a new one
- pool is closed
- ClassInstance expects an object instance
- notCallableMessage(method)
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/2fc65568faf1d91a.
Report an issue: GitHub.