pydantic/monty · error · Error

loadSession / loadSnapshot is only valid on a fresh session,

Error message

loadSession / loadSnapshot is only valid on a fresh session, before any feedRun / feedStart / loadSession / loadSnapshot

What it means

Snapshot restore (`loadSession`/`loadSnapshot`) is only valid on a brand-new session before any driving call. Once a session has run `feedRun`/`feedStart` or already loaded a snapshot, its worker state is no longer pristine, so restoring would corrupt it; an `Error` is thrown.

Source

Thrown at crates/monty-js/ts/session.ts:340

      | NativeTurn
      | LoadedTurn
    if (turn.kind === 'loaded') {
      throw await this.failedLoad(new Error('this dump is an idle session — use loadSession() to restore it'))
    }
    try {
      return await driver.advance(turn)
    } catch (err) {
      // any failure restoring the snapshot (bad mount, crash, protocol desync)
      // leaves the session unusable — poison it and release the worker
      throw await this.failedLoad(err instanceof Error ? err : new Error(String(err)))
    }
  }

  /** Claims a fresh session for a load (rejecting a reused one). */
  private claimFresh(): void {
    this.ensureUsable()
    if (this.driven) {
      throw new Error(
        'loadSession / loadSnapshot is only valid on a fresh session, before any feedRun / feedStart / loadSession / loadSnapshot',
      )
    }
    this.driven = true
  }

  /**
   * Poisons the session and releases its worker after a failed load, so any
   * later op fails like a crashed session — a failed load is not retryable.
   * Returns the error to throw.
   */
  private async failedLoad(err: Error): Promise<Error> {
    this.poison(err)
    try {
      await this.native.finish()
    } catch {
      // the worker was already discarded (e.g. it crashed) — nothing to release
    }

View on GitHub (pinned to adc986b362)

Solutions

  1. Check out a fresh session (`pool.checkout()`) for each `loadSession`/`loadSnapshot` call
  2. Load the snapshot as the very first operation on the session
  3. If resuming after failure, close the old session and check out a new one before loading

Example fix

// before
const session = await pool.checkout()
await session.feedRun('x = 1')
await session.loadSnapshot(snapshot) // throws
// after
const fresh = await pool.checkout()
await fresh.loadSnapshot(snapshot)
Defensive patterns

Strategy: validation

Validate before calling

if (session.hasDriven /* track a flag: any feedRun/feedStart/load* returns true */) {
  session = await pool.checkout()
}
await session.loadSnapshot(snapshot)

Type guard

function isFreshSession(session: MontySession, driven: WeakSet<MontySession>): boolean {
  return !driven.has(session)
}

Try / catch

try {
  await session.loadSession(data)
} catch (e) {
  if (e instanceof Error && e.message.includes('only valid on a fresh session')) {
    session = await pool.checkout()
    await session.loadSession(data)
  } else throw e
}

Prevention

When it happens

Trigger: Calling `session.loadSession(...)` after a prior `feedRun`/`feedStart`, or calling `loadSnapshot` twice on the same session; also reusing a pooled session returned from a previous checkout's logical flow.

Common situations: Replaying snapshots in a loop reusing one session object, resuming a saved session into a session that already ran warm-up code, retry logic that reloads a snapshot after a failed turn.

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/f6630dd4a663d678. Report an issue: GitHub.