denoland/deno · error · Error
Unable to deserialize RecordableHistogram
Error message
Unable to deserialize RecordableHistogram
What it means
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.
Source
Thrown at ext/node/polyfills/perf_hooks.js:606
}
return data.max;
},
percentileBigInt: (p) => {
const entries = data.percentilesBigInt ?? [[100, "0"]];
for (let i = 0; i < entries.length; i++) {
if (entries[i][0] >= p) return BigInt(entries[i][1]);
}
return BigInt(data.maxBigInt);
},
reset() {},
});
}
// Registered eagerly from `02_register_cloneable.js`; impl stays lazy here.
function deserializeRecordableHistogram(data) {
const handle = MapPrototypeGet(histogramCloneRegistry, data.id);
if (handle === undefined) {
throw new Error("Unable to deserialize RecordableHistogram");
}
return new RecordableHistogram(handle, data.id);
}
function validateInteger(value, name, min, max) {
if (typeof value === "bigint") {
if (value < BigInt(min) || value > BigInt(max)) {
throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);
}
return Number(value);
}
if (typeof value !== "number" || !NumberIsInteger(value)) {
throw new ERR_INVALID_ARG_TYPE(name, ["integer", "bigint"], value);
}
if (value < min || value > max) {
throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value);
}
return value;View on GitHub (pinned to 9ad36f7a2c)
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
Example fix
// before
import { parentPort } from "node:worker_threads";
parentPort.on("message", (h) => h.record(100n)); // may fail to deserialize
// after
import { parentPort } from "node:worker_threads";
import { createHistogram } from "node:perf_hooks";
const local = createHistogram();
parentPort.on("message", (n) => local.record(n)); Defensive patterns
Strategy: fallback
Validate before calling
// Before using anything received from another isolate:
const RecordableHistogram = require("node:perf_hooks").createHistogram().constructor;
const looksLikeHistogram = (v) => v instanceof RecordableHistogram;
// If false, treat it as plain data and rebuild rather than calling record/add on it Type guard
const isUsableHistogram = (v) =>
v instanceof require("node:perf_hooks").createHistogram().constructor; Try / catch
try {
received.record(n);
} catch (err) {
if (err.message === "Unable to deserialize RecordableHistogram") {
localHistogram.record(n); // fallback: keep a locally created histogram
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- ERR_ILLEGAL_CONSTRUCTOR
- ERR_OUT_OF_RANGE
- ERR_WORKER_UNSUPPORTED_OPERATION
- DataCloneError
- ${prefix}Linter plugin name must only contain lowercase lett
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/898c8bc6dcb85471.
Report an issue: GitHub.