BabylonJS/Babylon.js · error · Error

Command '${descriptor.id}' is already registered.

Error message

Command '${descriptor.id}' is already registered.

What it means

BridgeService's registry addCommand enforces unique command IDs: if commands.has(descriptor.id) the registration throws. Command IDs are primary keys for CLI dispatch, so duplicates would make routing ambiguous.

Source

Thrown at packages/dev/sharedUiComponents/src/modularTool/services/cli/bridgeService.ts:204

                            sendToBridge({
                                type: "commandResponse",
                                requestId: message.requestId,
                                error: String(error),
                            });
                        }
                        break;
                    }
                }
            }

            if (enabled) {
                connect();
            }

            const registry: IBridgeCommandRegistry & ICliConnectionStatus & IDisposable = {
                addCommand(descriptor: BridgeCommandDescriptor): IDisposable {
                    if (commands.has(descriptor.id)) {
                        throw new Error(`Command '${descriptor.id}' is already registered.`);
                    }
                    commands.set(descriptor.id, descriptor);
                    return {
                        dispose: () => {
                            commands.delete(descriptor.id);
                        },
                    };
                },
                get isEnabled() {
                    return enabled;
                },
                set isEnabled(value: boolean) {
                    if (enabled !== value) {
                        enabled = value;
                        if (enabled) {
                            connect();
                        } else {
                            disconnect();

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Dispose the IDisposable returned by the first addCommand before registering again.
  2. Check commands.has(id) (or wrap registration) before calling addCommand.
  3. Namespace command ids per extension (e.g. 'myext.command') to avoid collisions.
  4. On reconnect/hot-reload, rebuild or clear the registration set instead of re-adding.

Example fix

// before
bridge.addCommand({ id: "build", ... });
bridge.addCommand({ id: "build", ... }); // throws
// after
const reg = bridge.addCommand({ id: "build", ... });
// on re-registration:
reg.dispose();
bridge.addCommand({ id: "build", ... });
Defensive patterns

Strategy: validation

Validate before calling

function addCommandOnce(registry, descriptor) {
  if (registeredIds.has(descriptor.id)) return; // skip duplicate
  const handle = registry.addCommand(descriptor);
  registeredIds.add(descriptor.id);
  return {
    dispose: () => {
      registeredIds.delete(descriptor.id);
      handle.dispose();
    },
  };
}

Try / catch

// try {
//   return registry.addCommand(descriptor);
// } catch (e) {
//   if (String(e.message).includes("is already registered")) return existingHandle; // idempotent
//   throw e;
// }

Prevention

When it happens

Trigger: Calling addCommand twice with descriptors sharing the same id; re-registering after reconnect without first disposing the previous registration handle; two modules registering a command with the same id string.

Common situations: Plugin hot-reload re-running registration code against a persistent registry; naming collisions between built-in and extension commands; forgetting to call dispose() on the IDisposable returned by addCommand before re-registering.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/9755272394183b5f. Report an issue: GitHub.