denoland/deno · error · Error
${name} is already registered
Error message
${name} is already registered What it means
Deno.core.registerTransferableResource(name, send, receive) installs the serializer/deserializer pair used when a resource of that kind crosses a worker postMessage boundary. Names key a single global registry in 01_core.js and must be unique; registering an existing name throws `Error: ${name} is already registered` because structured clone could not decide which receive function to trust. This is an internal API used when building extensions or custom runtime snapshots.
Source
Thrown at libs/core/01_core.js:727
op_shutdown: shutdown,
op_is_terminal: isTerminal,
} = ops;
const callSiteRetBuf = new Uint32Array(2);
const callSiteRetBufU8 = new Uint8Array(callSiteRetBuf.buffer);
function currentUserCallSite() {
const fileName = op_current_user_call_site(callSiteRetBufU8);
const lineNumber = callSiteRetBuf[0];
const columnNumber = callSiteRetBuf[1];
return { fileName, lineNumber, columnNumber };
}
const hostObjectBrand = SymbolFor("Deno.core.hostObject");
const transferableResources = {};
const registerTransferableResource = (name, send, receive) => {
if (transferableResources[name]) {
throw new Error(`${name} is already registered`);
}
transferableResources[name] = { send, receive };
};
const getTransferableResource = (name) => transferableResources[name];
const cloneableDeserializers = { __proto__: null };
const registerCloneableResource = (name, deserialize) => {
if (cloneableDeserializers[name]) {
throw new Error(`${name} is already registered`);
}
cloneableDeserializers[name] = deserialize;
};
const getCloneableDeserializers = () => cloneableDeserializers;
// A helper function that will bind our own console implementation
// with default implementation of Console from V8. This will cause
// console messages to be piped to inspector console.
//
// We are using `Deno.core.callConsole` binding to preserve proper stackView on GitHub (pinned to 9ad36f7a2c)
Solutions
- Give each registration a unique, namespaced name such as 'myExt.myResource'
- Check `core.getTransferableResource(name)` is undefined first, and skip or fail with a clearer diagnostic
- Make the extension's init script idempotent and ensure it loads exactly once (deduplicate the extension list)
Example fix
// before
core.registerTransferableResource('blob', send, receive); // second registration of 'blob' throws
// after
if (core.getTransferableResource('myExt.blob') === undefined) {
core.registerTransferableResource('myExt.blob', send, receive);
} Defensive patterns
Strategy: validation
Validate before calling
if (core.getTransferableResource(name) !== undefined) {
throw new Error(`transferable resource '${name}' is already registered`);
}
core.registerTransferableResource(name, send, receive); Prevention
- Namespace resource names by extension (myExt.resource) so collisions cannot happen silently
- Register in exactly one place during extension init; never re-run init scripts
- Fail fast at startup on duplicate registrations so the offending extension is obvious
When it happens
Trigger: Two extensions registering a transferable resource under the same name during snapshot bootstrap; an extension's init JavaScript being evaluated twice; a forked/copied extension keeping the original resource name.
Common situations: Building custom runtimes with deno_core's ExtensionBuilder JS snippets; name collisions between first-party and third-party extensions (e.g. two variants both registering a body/file resource); duplicate bootstrap in tests.
Related errors
- JS PANIC: {}
- Attempted to read snapshot data out of range: {id} (of {})
- Attempted to read the snapshot data at index {id} twice
- Linter plugin ${plugin.name} has already been registered
- expected a function
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/52346bee64ce4e86.
Report an issue: GitHub.