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

  1. Create a new pool with `Monty.create(...)` before checking out
  2. Reorder code so all checkouts happen inside the pool's `await using` block
  3. 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

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


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/2fc65568faf1d91a. Report an issue: GitHub.