{"record":{"id":"898c8bc6dcb85471","repo":"denoland/deno","slug":"unable-to-deserialize-recordablehistogram","errorCode":null,"errorMessage":"Unable to deserialize RecordableHistogram","messagePattern":"Unable to deserialize RecordableHistogram","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/perf_hooks.js","lineNumber":606,"sourceCode":"      }\n      return data.max;\n    },\n    percentileBigInt: (p) => {\n      const entries = data.percentilesBigInt ?? [[100, \"0\"]];\n      for (let i = 0; i < entries.length; i++) {\n        if (entries[i][0] >= p) return BigInt(entries[i][1]);\n      }\n      return BigInt(data.maxBigInt);\n    },\n    reset() {},\n  });\n}\n\n// Registered eagerly from `02_register_cloneable.js`; impl stays lazy here.\nfunction deserializeRecordableHistogram(data) {\n  const handle = MapPrototypeGet(histogramCloneRegistry, data.id);\n  if (handle === undefined) {\n    throw new Error(\"Unable to deserialize RecordableHistogram\");\n  }\n  return new RecordableHistogram(handle, data.id);\n}\n\nfunction validateInteger(value, name, min, max) {\n  if (typeof value === \"bigint\") {\n    if (value < BigInt(min) || value > BigInt(max)) {\n      throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);\n    }\n    return Number(value);\n  }\n  if (typeof value !== \"number\" || !NumberIsInteger(value)) {\n    throw new ERR_INVALID_ARG_TYPE(name, [\"integer\", \"bigint\"], value);\n  }\n  if (value < min || value > max) {\n    throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);\n  }\n  return value;","sourceCodeStart":588,"sourceCodeEnd":624,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/perf_hooks.js#L588-L624","documentation":"When a RecordableHistogram crosses an isolate boundary (worker.postMessage / structuredClone), Deno's clone registry (02_register_cloneable.js) re-attaches the native handle by clone id. If the target isolate's registry has no entry for that id, deserializeRecordableHistogram throws this plain Error — it has no error code, so it is easy to miss in code-based handling.","triggerScenarios":"worker.postMessage(histogram) followed by calling record()/add() on it inside the worker; structuredClone(histogram) then using the result; returning a histogram from a worker to the main thread; using a histogram whose originating worker already exited.","commonSituations":"Collecting latency metrics from worker pools; Deno version skew or registration-order differences between the two isolates; libraries that transparently structured-clone arguments (NestedWorker, puppeteer-style helpers).","solutions":["Don't ship RecordableHistogram objects across isolates — create a fresh histogram inside each worker with createHistogram() and record locally","When you need aggregates on the main thread, send plain numbers (percentiles, mean, count) and combine them there","If you must move one, re-create the histogram at the destination and replay/merge values via record()","Catch this un-coded Error by message when postMessage-adjacent code handles failures"],"exampleFix":"// before\nimport { parentPort } from \"node:worker_threads\";\nparentPort.on(\"message\", (h) => h.record(100n)); // may fail to deserialize\n\n// after\nimport { parentPort } from \"node:worker_threads\";\nimport { createHistogram } from \"node:perf_hooks\";\nconst local = createHistogram();\nparentPort.on(\"message\", (n) => local.record(n));","handlingStrategy":"fallback","validationCode":"// Before using anything received from another isolate:\nconst RecordableHistogram = require(\"node:perf_hooks\").createHistogram().constructor;\nconst looksLikeHistogram = (v) => v instanceof RecordableHistogram;\n// If false, treat it as plain data and rebuild rather than calling record/add on it","typeGuard":"const isUsableHistogram = (v) =>\n  v instanceof require(\"node:perf_hooks\").createHistogram().constructor;","tryCatchPattern":"try {\n  received.record(n);\n} catch (err) {\n  if (err.message === \"Unable to deserialize RecordableHistogram\") {\n    localHistogram.record(n); // fallback: keep a locally created histogram\n  } else throw err;\n}","preventionTips":["Never rely on histogram objects surviving worker/structuredClone boundaries — create them per isolate","Send plain numbers (count, mean, percentiles) across isolates and aggregate numerically","This error has no `code` property — match on message when you must catch it"],"tags":["perf-hooks","histogram","structured-clone","worker-threads","deno"],"backgroundTag":"structured-clone-deserialization-failed","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}