{"record":{"id":"ae13494c05736dd9","repo":"agalwood/Motrix","slug":"plugin-commands-id-out-of-namespace","errorCode":"plugin.commands.id_out_of_namespace","errorMessage":"command \"${commandId}\" is outside namespace \"${callerId}.\" — commands must start with \"${callerId}.\"","messagePattern":"command \"(.+?)\" is outside namespace \"(.+?)\\.\" — commands must start with \"(.+?)\\.\"","errorType":"validation","errorClass":"CommandsError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/capabilities/commands.ts","lineNumber":114,"sourceCode":"  /**\n   * Register `handler` for `commandId`. The `commandId` MUST start with\n   * `${callerId}.` — i.e. belong to the caller's own namespace. When a\n   * manifest resolver is configured, `commandId` must also be declared in\n   * the caller's `contributes.commands[]`. Returns a registration whose\n   * `dispose()` removes only this command. Repeated registration of the\n   * same id replaces the previous handler and emits `console.warn` (spec\n   * §5 L1742, consistent with the hooks contract).\n   *\n   * @throws {CommandsError} plugin.commands.id_out_of_namespace — wrong owner\n   * @throws {CommandsError} plugin.command.not_declared_in_manifest — missing from manifest\n   */\n  register(\n    callerId: string,\n    commandId: string,\n    handler: CommandHandler\n  ): CommandsRegistration {\n    if (!commandId.startsWith(`${callerId}.`)) {\n      throw new CommandsError(\n        'plugin.commands.id_out_of_namespace',\n        `command \"${commandId}\" is outside namespace \"${callerId}.\" — commands must start with \"${callerId}.\"`\n      )\n    }\n\n    if (this.resolveDeclared) {\n      const declared = this.resolveDeclared(callerId)\n      if (!declared?.has(commandId)) {\n        throw new CommandsError(\n          'plugin.command.not_declared_in_manifest',\n          `command \"${commandId}\" is not declared in plugin \"${callerId}\" manifest.contributes.commands[]`\n        )\n      }\n    }\n\n    if (this.handlers.has(commandId)) {\n      console.warn(\n        `[plugin:commands] handler for \"${commandId}\" registered more than once; previous handler replaced`","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/capabilities/commands.ts#L96-L132","documentation":"CommandsError with code 'plugin.commands.id_out_of_namespace', thrown by CommandsCapabilityHost.register when commandId does not start with `${callerId}.`. The capability enforces namespacing so a plugin can only register commands it owns (spec §5 L1741-1746), preventing one plugin from shadowing another's command IDs. callerId is the plugin's own identifier.","triggerScenarios":"Calling host.register('myPlugin', 'otherPlugin.doThing', handler) — i.e. a plugin tries to register a command in another plugin's namespace, or in no namespace at all ('doThing' without a prefix). Also thrown if callerId is mistyped or empty so the prefix does not match.","commonSituations":"Copy-paste of a command ID from a different plugin; plugin id renamed but command IDs not updated; forgot to prefix with the plugin name; inconsistent callerId between activate() and register() (e.g. one uses 'foo', the other 'foo.bar').","solutions":["Ensure every commandId passed to register is of the form `${pluginId}.${localName}`.","Derive the prefix from the same callerId used in activate() — avoid hardcoding a different string.","Add a unit test asserting all registered IDs start with the plugin's own id.","If you need to invoke another plugin's command, use the cross-plugin execute() path, not register()."],"exampleFix":"// before\nhost.register('myPlugin', 'doThing', handler)\n// after\nhost.register('myPlugin', 'myPlugin.doThing', handler)","handlingStrategy":"validation","validationCode":"function isInOwnNamespace(callerId: string, commandId: string): boolean {\n  return commandId.startsWith(`${callerId}.`)\n}\nif (!isInOwnNamespace(callerId, commandId)) {\n  throw new Error(`command ${commandId} must be in namespace ${callerId}.*`)\n}\nhost.register(callerId, commandId, handler)","typeGuard":"function isInOwnNamespace(callerId: string, commandId: string): boolean {\n  return commandId.startsWith(`${callerId}.`)\n}","tryCatchPattern":"try {\n  host.register(callerId, commandId, handler)\n} catch (e) {\n  if (e instanceof CommandsError && e.code === 'plugin.commands.id_out_of_namespace') {\n    // fix the commandId prefix and retry\n  } else throw e\n}","preventionTips":["Derive command IDs from the same callerId string used in activate().","Add a unit test asserting every registered command starts with `${pluginId}.`.","Use the cross-plugin execute() path to invoke other plugins' commands, never register()."],"tags":["plugin","commands","namespacing","api-contract"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}