{"record":{"id":"421d1c975659cb3a","repo":"microsoft/playwright","slug":"jshandle-is-disposed","errorCode":null,"errorMessage":"JSHandle is disposed!","messagePattern":"JSHandle is disposed!","errorType":"exception","errorClass":"JavaScriptErrorInEvaluate","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/javascript.ts","lineNumber":267,"sourceCode":"export async function evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {\n  return evaluateExpression(context, String(pageFunction), { returnByValue, isFunction: typeof pageFunction === 'function' }, ...args);\n}\n\nexport async function evaluateExpression(context: ExecutionContext, expression: string, options: { returnByValue?: boolean, isFunction?: boolean }, ...args: any[]): Promise<any> {\n  expression = normalizeEvaluationExpression(expression, options.isFunction);\n  const handles: (Promise<JSHandle>)[] = [];\n  const toDispose: Promise<JSHandle>[] = [];\n  const pushHandle = (handle: Promise<JSHandle>): number => {\n    handles.push(handle);\n    return handles.length - 1;\n  };\n\n  args = args.map(arg => serializeAsCallArgument(arg, handle => {\n    if (handle instanceof JSHandle) {\n      if (!handle._objectId)\n        return { fallThrough: handle._value };\n      if (handle._disposed)\n        throw new JavaScriptErrorInEvaluate('JSHandle is disposed!');\n      const adopted = context.adoptIfNeeded(handle);\n      if (adopted === null)\n        return { h: pushHandle(Promise.resolve(handle)) };\n      toDispose.push(adopted);\n      return { h: pushHandle(adopted) };\n    }\n    return { fallThrough: handle };\n  }));\n\n  const utilityScriptObjects: JSHandle[] = [];\n  for (const handle of await Promise.all(handles)) {\n    if (handle._context !== context)\n      throw new JavaScriptErrorInEvaluate('JSHandles can be evaluated only in the context they were created!');\n    utilityScriptObjects.push(handle);\n  }\n\n  // See UtilityScript for arguments.\n  const utilityScriptValues = [options.isFunction, options.returnByValue, expression, args.length, ...args];","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/javascript.ts#L249-L285","documentation":"Thrown as a JavaScriptErrorInEvaluate by evaluateExpression() when serializing a call argument that is a JSHandle with an _objectId (i.e., a remote object reference) but whose _disposed flag is true. This means the handle was previously disposed (via handle.dispose() or context/page navigation) but was still passed as an argument to an evaluate call.","triggerScenarios":"Passing a JSHandle to page.evaluate(), element.evaluate(), or similar after calling handle.dispose() on it. Also occurs when the handle's execution context was destroyed by a navigation and the handle was implicitly disposed. The serialization callback checks _disposed before attempting to use the handle.","commonSituations":"Storing a JSHandle in a variable, disposing it, then later passing it to another evaluate call. Navigation invalidates handles created before the navigation. Using a handle from a previous test step after the page navigated. Explicitly calling dispose() too early in a test helper.","solutions":["Do not pass disposed handles to evaluate. Re-query the element or re-create the handle before use.","Avoid storing JSHandles across navigation boundaries; re-query after page.goto() or click-induced navigation.","Remove explicit dispose() calls if the handle is still needed downstream — Playwright GCs handles automatically.","Use page.evaluate() with serializable arguments instead of JSHandles when possible."],"exampleFix":"// before\nconst handle = await page.evaluateHandle('document');\nawait handle.dispose();\nawait page.evaluate(h => h.title, handle); // throws [334]\n\n// after\nconst handle = await page.evaluateHandle('document');\nawait page.evaluate(h => h.title, handle);\nawait handle.dispose(); // dispose after last use","handlingStrategy":"validation","validationCode":"// Check handle before use\nif (handle._disposed) // or track disposal manually\n  throw new Error('Handle already disposed');\n// Better: re-query the handle\nconst freshHandle = await page.evaluateHandle('document');","typeGuard":"function isHandleAlive(handle: import('@playwright/test').JSHandle): boolean {\n  // There is no public isDisposed; track disposal via try/catch on jsonValue()\n  return true; // best practice: re-query instead of checking\n}","tryCatchPattern":"try {\n  await page.evaluate(fn, handle);\n} catch (e) {\n  if (e.message === 'JSHandle is disposed!') {\n    // Re-query the element/handle and retry\n    const fresh = await page.locator(selector).elementHandle();\n    return page.evaluate(fn, fresh);\n  }\n  throw e;\n}","preventionTips":["Never pass disposed handles to evaluate.","Re-query elements after navigation instead of reusing old handles.","Avoid premature dispose() calls; let Playwright manage handle lifecycle."],"tags":["jshandle","evaluate","disposed","javascript","lifecycle"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}