agalwood/Motrix · error · CommandsError

plugin.command.not_declared_in_manifest

plugin.command.not_declared_in_manifest

Error message

command "${commandId}" is not declared in plugin "${callerId}" manifest.contributes.commands[]

What it means

CommandsError with code 'plugin.command.not_declared_in_manifest', thrown by CommandsCapabilityHost.register when commandId is in the caller's namespace but is not present in the set returned by the manifestCommandIds resolver for that caller. The capability requires that every registered command be statically declared in the plugin's manifest.contributes.commands[] — a manifest-driven contract enforced only when a manifestCommandIds resolver is wired (production); in tests without a resolver this check is skipped for back-compat.

Source

Thrown at src/core/plugin/capabilities/commands.ts:123

   * @throws {CommandsError} plugin.commands.id_out_of_namespace — wrong owner
   * @throws {CommandsError} plugin.command.not_declared_in_manifest — missing from manifest
   */
  register(
    callerId: string,
    commandId: string,
    handler: CommandHandler
  ): CommandsRegistration {
    if (!commandId.startsWith(`${callerId}.`)) {
      throw new CommandsError(
        'plugin.commands.id_out_of_namespace',
        `command "${commandId}" is outside namespace "${callerId}." — commands must start with "${callerId}."`
      )
    }

    if (this.resolveDeclared) {
      const declared = this.resolveDeclared(callerId)
      if (!declared?.has(commandId)) {
        throw new CommandsError(
          'plugin.command.not_declared_in_manifest',
          `command "${commandId}" is not declared in plugin "${callerId}" manifest.contributes.commands[]`
        )
      }
    }

    if (this.handlers.has(commandId)) {
      console.warn(
        `[plugin:commands] handler for "${commandId}" registered more than once; previous handler replaced`
      )
    }

    this.handlers.set(commandId, handler)

    return {
      dispose: () => {
        this.handlers.delete(commandId)
      },

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Add the command ID to manifest.contributes.commands[] for the plugin.
  2. Confirm exact string match (case, dot, no trailing whitespace) between the manifest entry and the registered commandId.
  3. Reload/rebuild the manifest resolver so it serves the updated set.
  4. In tests where you intentionally skip declaration, construct CommandsCapabilityHost without manifestCommandIds.

Example fix

// before — manifest.contributes.commands = [{ id: 'myPlugin.other' }]
host.register('myPlugin', 'myPlugin.doThing', handler)
// after — declare it in the manifest
// manifest.contributes.commands = [
//   { id: 'myPlugin.other' },
//   { id: 'myPlugin.doThing' },
// ]
host.register('myPlugin', 'myPlugin.doThing', handler)
Defensive patterns

Strategy: validation

Validate before calling

function isDeclaredInManifest(
  declared: ReadonlySet<string> | undefined,
  commandId: string
): boolean {
  return !declared || declared.has(commandId)
}
const declared = manifestCommandIds?.(callerId)
if (!isDeclaredInManifest(declared, commandId)) {
  throw new Error(`${commandId} missing from manifest.contributes.commands[]`)
}
host.register(callerId, commandId, handler)

Type guard

function isDeclaredInManifest(declared: ReadonlySet<string> | undefined, commandId: string): boolean {
  return !declared || declared.has(commandId)
}

Try / catch

try {
  host.register(callerId, commandId, handler)
} catch (e) {
  if (e instanceof CommandsError && e.code === 'plugin.command.not_declared_in_manifest') {
    // add commandId to manifest.contributes.commands[] and reload
  } else throw e
}

Prevention

When it happens

Trigger: Calling host.register('myPlugin', 'myPlugin.doThing', handler) where 'myPlugin.doThing' is not listed in the plugin's manifest.contributes.commands[] AND the host was constructed with a manifestCommandIds resolver (as capability-host.ts does in production). Distinguishable from error 78 because the namespace check passed.

Common situations: A new command was added in code but the manifest was not updated; the manifest entry has a typo or different casing; the plugin id in the manifest differs from the callerId passed to register; manifest reload did not pick up a freshly added command; resolver returns a stale snapshot.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/b8a86cf05a712eda. Report an issue: GitHub.