{"record":{"id":"2118333ecfacca0c","repo":"grafana/k6","slug":"retrieving-json-value-w","errorCode":null,"errorMessage":"retrieving json value: %w","messagePattern":"retrieving json value: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/js_handle.go","lineNumber":202,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t\tprops[r.Name] = NewJSHandle(h.ctx, h.session, h.execCtx, h.execCtx.Frame(), r.Value, h.logger)\n\t}\n\n\treturn props, nil\n}\n\n// JSONValue returns a JSON version of this JS handle.\nfunc (h *BaseJSHandle) JSONValue() (string, error) {\n\tremoteObject := h.remoteObject\n\tif remoteObject.ObjectID != \"\" {\n\t\tvar err error\n\t\taction := runtime.CallFunctionOn(\"function() { return this; }\").\n\t\t\tWithReturnByValue(true).\n\t\t\tWithAwaitPromise(true).\n\t\t\tWithObjectID(h.remoteObject.ObjectID)\n\t\tif remoteObject, _, err = action.Do(cdp.WithExecutor(h.ctx, h.session)); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"retrieving json value: %w\", err)\n\t\t}\n\t}\n\n\tres, err := parseConsoleRemoteObject(h.logger, remoteObject)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"extracting json value (remote object id: %v): %w\", remoteObject.ObjectID, err)\n\t}\n\n\treturn res, nil\n}\n\n// ObjectID returns the remote object ID.\nfunc (h *BaseJSHandle) ObjectID() runtime.RemoteObjectID {\n\treturn h.remoteObject.ObjectID\n}\n","sourceCodeStart":184,"sourceCodeEnd":218,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/js_handle.go#L184-L218","documentation":"Thrown by BaseJSHandle.JSONValue when the CDP Runtime.callFunctionOn('function() { return this; }') round-trip fails for an object-backed handle. The handle's remote object ID is no longer resolvable by the browser (released, GC'd, or its context destroyed), so the protocol call itself errors before any JSON parsing happens.","triggerScenarios":"Calling jsonValue() on a handle whose object was garbage-collected by the browser; using a handle after navigation destroyed its execution context; calling jsonValue() after dispose(); page or session closed during the call.","commonSituations":"Extracting text or attributes via handle.jsonValue() from handles kept alive across multiple page transitions; long test iterations where VU-level timeouts cancel the session mid-call.","solutions":["Call jsonValue() immediately after obtaining the handle, before any navigation","Re-query the element and retry once if the handle has gone stale","Do not call dispose() before extracting values","Prefer locator.textContent()/innerText() style helpers which re-resolve the element per call"],"exampleFix":"// before\nconst h = await page.$('h1');\nawait page.goto('https://example.com/next');  // invalidates handle\nconst text = await h.jsonValue();\n\n// after\nawait page.goto('https://example.com/next');\nconst h = await page.$('h1');\nconst text = await h.jsonValue();","handlingStrategy":"try-catch","validationCode":"if (page.isClosed()) throw new Error('page closed before jsonValue');","typeGuard":null,"tryCatchPattern":"try {\n  return await handle.jsonValue();\n} catch (e) {\n  if (/retrieving json value/.test(e.message)) {\n    const fresh = await page.$(selector);\n    return fresh.jsonValue(); // stale remote object: re-acquire once\n  }\n  throw e;\n}","preventionTips":["Extract values immediately after obtaining the handle","Prefer element accessors (textContent(), getAttribute()) over jsonValue() on elements","Do not dispose handles before reading their values"],"tags":["browser","cdp","js-handle","serialization","stale-reference"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}