{"record":{"id":"dececde1a990f9d6","repo":"grafana/k6","slug":"optionhandle-dispose-w","errorCode":null,"errorMessage":"optionHandle.dispose: %w","messagePattern":"optionHandle\\.dispose: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":1744,"sourceCode":"\tselectHandle, ok := v.(jsHandle)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unexpected select element type %T\", v)\n\t}\n\n\t// pack the selected <option> values inside <select> into a slice\n\toptHandles, err := selectHandle.getProperties()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getProperties: %w\", err)\n\t}\n\tvals := make([]string, 0, len(optHandles))\n\tfor _, oh := range optHandles {\n\t\tval, err := oh.JSONValue()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"reading value: %w\", err)\n\t\t}\n\t\tvals = append(vals, val)\n\t\tif err := oh.dispose(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"optionHandle.dispose: %w\", err)\n\t\t}\n\t}\n\tif err := selectHandle.dispose(); err != nil {\n\t\treturn nil, fmt.Errorf(\"selectHandle.dispose: %w\", err)\n\t}\n\n\treturn vals, nil\n}\n\n// SetContent replaces the entire HTML document content.\nfunc (f *Frame) SetContent(html string, _ *FrameSetContentOptions) error {\n\tf.log.Debugf(\"Frame:SetContent\", \"fid:%s furl:%q\", f.ID(), f.URL())\n\n\t// TODO(@inancgumus): Respect the FrameSetContentOptions before executing the action.\n\t// A solution is similar to the logic in `WaitForLoadState`.\n\n\tjs := `(html) => {\n\t\twindow.stop();","sourceCodeStart":1726,"sourceCodeEnd":1762,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L1726-L1762","documentation":"After reading each selected option's value, k6 disposes the option's remote object; a failed Runtime.releaseObject surfaces as 'optionHandle.dispose: <cause>'. This is a resource-cleanup error after the useful work already succeeded — the value was read, but the CDP object could not be released.","triggerScenarios":"The option's execution context is destroyed between JSONValue() and dispose() (navigation, re-render); the remote object was already garbage-collected or force-released by the browser; the page is closing.","commonSituations":"Same class as the readback errors: frameworks that rebuild the select on change; page.close() racing the call; mostly harmless unless it fires on every selection, which signals a lifecycle race worth fixing.","solutions":["If occasional, wrap selectOption and verify the selection afterwards via evaluate instead of trusting the return path.","Avoid closing the page or navigating immediately after selectOption.","If it fires consistently, update k6 — dispose ordering in the selectOption readback has been improved across releases."],"exampleFix":"// before\nconst v = await page.selectOption('#sel', 'x');\nawait page.close(); // races dispose\n\n// after\nconst v = await page.selectOption('#sel', 'x');\nawait page.waitForTimeout(50); // let cleanup finish\nawait page.close();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { return await page.selectOption(sel, v); }\ncatch (e) {\n  if (/optionHandle\\.dispose/.test(e.message)) { /* values were read; verify via evaluate */\n    return page.evaluate(() => [...document.querySelector(sel).selectedOptions].map(o => o.value));\n  }\n  throw e;\n}","preventionTips":["Treat dispose failures as post-success cleanup noise; verify state instead","Avoid navigating or closing the page during selectOption","Upgrade k6 if dispose errors fire on every selection"],"tags":["browser","select","cleanup","cdp","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}