{"record":{"id":"eeed097da81f1742","repo":"pydantic/monty","slug":"snapshot-has-already-been-resumed","errorCode":null,"errorMessage":"snapshot has already been resumed","messagePattern":"snapshot has already been resumed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/session.ts","lineNumber":923,"sourceCode":"      (await this.native.resumeNameLookup(null, { value: prepare(value, this.instances) }, this.onPrint)) as NativeTurn,\n    )\n  }\n\n  async resolveFutures(results: NativeFutureResult[]): Promise<Snapshot> {\n    return this.advance((await this.native.resolveFutures(results, this.onPrint)) as NativeTurn)\n  }\n\n  async dump(): Promise<Buffer> {\n    return bufferFrom(await this.native.dump())\n  }\n}\n\n/** Marks a snapshot single-use: each may be resumed at most once. */\nclass SingleUse {\n  private used = false\n  protected claim(): void {\n    if (this.used) {\n      throw new Error('snapshot has already been resumed')\n    }\n    this.used = true\n  }\n}\n\n/**\n * A paused execution waiting for an external function or OS call result. For\n * OS calls `isOsFunction` is `true`; resume with a value, an error, or\n * `resumeNotHandled()`.\n */\nexport class FunctionSnapshot extends SingleUse {\n  readonly functionName: string\n  /** Positional arguments, already converted to JS values. */\n  readonly args: unknown[]\n  /** Keyword arguments (null-prototype record; string keys only). */\n  readonly kwargs: Record<string, unknown>\n  readonly callId: number\n  readonly isOsFunction: boolean","sourceCodeStart":905,"sourceCodeEnd":941,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/session.ts#L905-L941","documentation":"The `SingleUse` class marks each snapshot as resumable at most once. `claim()` throws this Error when code attempts to resume a snapshot that has already been consumed, preventing double-resume of a paused worker turn. Snapshots are single-use by design: the underlying worker state can only advance once per captured point.","triggerScenarios":"Calling resumeReturn/resumeError/resumeNotHandled/resumeFuture (or any Snapshot resume method) twice on the same Snapshot object; storing a snapshot and re-resuming it after a previous resume already claimed it.","commonSituations":"Retry logic that captures a snapshot, resumes it, catches an error in the host callback, then tries to resume the same snapshot again with a different answer; sharing a snapshot across concurrent async tasks that both attempt resume.","solutions":["Resume each Snapshot exactly once; capture a fresh snapshot for every attempt by re-running or re-loading.","Track claimed state in your own code (e.g. a Set of consumed snapshots) before calling resume.","If you need to retry with a different resume value, reload the snapshot via `session.loadSnapshot` to obtain a fresh, unclaimed instance if supported, or re-feed the code."],"exampleFix":"// before\nconst snap = await session.resumeNotHandled()\nawait snap.resumeReturn(value)\nawait snap.resumeReturn(value) // throws\n\n// after\nconst snap = await session.resumeNotHandled()\nif (!consumed.has(snap)) {\n  consumed.add(snap)\n  await snap.resumeReturn(value)\n}","handlingStrategy":"validation","validationCode":"function canResume(snap: Snapshot, consumed: WeakSet<Snapshot>): boolean {\n  return !consumed.has(snap)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await snap.resumeReturn(value)\n} catch (err) {\n  if (err instanceof Error && err.message === 'snapshot has already been resumed') {\n    // obtain a fresh snapshot or skip\n  } else {\n    throw err\n  }\n}","preventionTips":["Treat Snapshot objects as single-use: resume once, then discard the reference.","Keep a WeakSet of consumed snapshots in any retry or multiplexing logic.","Never share one snapshot across concurrent async tasks without ownership transfer."],"tags":["typescript","snapshot","single-use","state-machine"],"backgroundTag":"invalid-state-transition","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}