{"record":{"id":"2e4dc6d6da8a8b2e","repo":"microsoft/playwright","slug":"unexpected-handle","errorCode":null,"errorMessage":"Unexpected handle","messagePattern":"Unexpected handle","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/protocol/src/serializers.ts","lineNumber":130,"sourceCode":"    return result;\n  }\n  if (value.me !== undefined) {\n    const result = new Map();\n    refs.set(value.id!, result);\n    for (const { k, v } of value.me)\n      result.set(innerParseSerializedValue(k, handles, refs, accessChain), innerParseSerializedValue(v, handles, refs, accessChain));\n    return result;\n  }\n  if (value.se !== undefined) {\n    const result = new Set();\n    refs.set(value.id!, result);\n    for (const item of value.se)\n      result.add(innerParseSerializedValue(item, handles, refs, accessChain));\n    return result;\n  }\n  if (value.h !== undefined) {\n    if (handles === undefined)\n      throw new Error('Unexpected handle');\n    return handles[value.h];\n  }\n  if (value.fn !== undefined) {\n    const dummy = () => {};\n    Object.defineProperty(dummy, 'name', { value: value.fn });\n    return dummy;\n  }\n  throw new Error(`Attempting to deserialize unexpected value${accessChainToDisplayString(accessChain)}: ${value}`);\n}\n\nexport type HandleOrValue = { h: number } | { fn: string } | { fallThrough: any };\ntype VisitorInfo = {\n  visited: Map<object, number>;\n  lastId: number;\n  serialize?: ('Map' | 'Set')[];\n};\n\nexport function serializeValue(value: any, handleSerializer: (value: any) => HandleOrValue, options: { serialize?: ('Map' | 'Set')[] } = {}): SerializedValue {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/microsoft/playwright/blob/f1d33b5029be8bbc1095fe88e559117fa3a6243b/packages/protocol/src/serializers.ts#L112-L148","documentation":"During protocol value deserialization, a payload can carry a handle reference (value.h) pointing to a JSHandle transferred between client and server. innerParseSerializedValue throws 'Unexpected handle' when a handle index arrives but no handles array was supplied to resolve it against — i.e. a value referencing an object handle is parsed in a context that does not carry the handles list.","triggerScenarios":"parseSerializedValue (or a nested Map/Set/function entry recursing into innerParseSerializedValue) encounters an 'h' field while its handles parameter is undefined.","commonSituations":"Custom protocol/channel code or tests hand-constructing serialized payloads, client/server serializer version skew, or passing JSHandles/functions through APIs that do not transfer handles.","solutions":["If writing protocol code, pass the message's handles array to parseSerializedValue so handle indices resolve.","Remove unsupported values (JSHandles, functions, unserializable objects) from data sent through APIs that do not transfer handles.","Rebuild the repo (npm run build) to fix generated protocol/serializer version skew.","Report upstream if triggered by ordinary public API usage — it indicates an internal invariant violation."],"exampleFix":"// before\nconst value = parseSerializedValue(payload, undefined);\n// after\nconst value = parseSerializedValue(payload, message.handles);","handlingStrategy":"try-catch","validationCode":"// Before parsing: only pass payloads expected to reference handles together with their handles list.\nif (payload.h !== undefined && handles === undefined)\n  throw new Error('Payload references a handle but no handles array was provided');","typeGuard":"function referencesHandle(v: any): boolean {\n  return v != null && (v.h !== undefined ||\n    (v.m?.some?.(referencesHandle)) || (v.se?.some?.(referencesHandle)));\n}","tryCatchPattern":"let parsed: unknown;\ntry {\n  parsed = parseSerializedValue(payload, handles);\n} catch (e) {\n  if ((e as Error).message === 'Unexpected handle') {\n    throw new Error('Protocol value references a handle in a handle-less context; regenerate protocol artifacts or pass the handles array.');\n  }\n  throw e;\n}","preventionTips":["Always pass the message's handles array alongside the serialized value.","Do not send JSHandles/functions through APIs that do not support handle transfer.","Run npm run build after protocol changes to avoid client/server serializer skew.","In tests, assert payload/handles pairing before calling parseSerializedValue."],"tags":["serialization","protocol","internal"],"backgroundTag":"internal-invariant-violation","analyzedSha":"f1d33b5029be8bbc1095fe88e559117fa3a6243b","analyzedAt":"2026-09-21T21:58:31.278Z","contentChangedAt":"2026-09-21T21:58:31.278Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}