agalwood/Motrix · error · PluginCodedError

plugin.capability.unavailable

plugin.capability.unavailable

Error message

unknown log method: ${msg.method}

What it means

Thrown by dispatchLog when the args parsed successfully but msg.method does not name a function on the host logger instance (looked up dynamically at capability-bridge.ts:604-606). Only log levels actually present on the logger are callable; any other method name is rejected as plugin.capability.unavailable.

Source

Thrown at src/core/plugin/host/capability-bridge.ts:608

  // -------------------------------------------------------------------------
  // log
  // -------------------------------------------------------------------------

  private dispatchLog(msg: BridgeCallMessage): void {
    const parsed = logArgsSchema.safeParse(msg.args)
    if (!parsed.success) {
      throw new PluginCodedError(
        'plugin.capability.bad_args',
        `log.${msg.method}: invalid args`
      )
    }
    const [text, ...rest] = parsed.data
    const fields = rest[0] as Record<string, unknown> | undefined
    const fn = (
      this.log as unknown as Record<string, (m: string, f?: object) => void>
    )[msg.method]
    if (typeof fn !== 'function') {
      throw new PluginCodedError(
        'plugin.capability.unavailable',
        `unknown log method: ${msg.method}`
      )
    }
    fn.call(this.log, String(text), fields)
  }

  // -------------------------------------------------------------------------
  // app — snapshot only; worker reads app.* from the init message.
  // Provide it here for completeness in case worker calls it post-init.
  // -------------------------------------------------------------------------

  private dispatchApp(_msg: BridgeCallMessage): unknown {
    return this.opts.capabilityHost.appSnapshot()
  }

  // -------------------------------------------------------------------------
  // i18n

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Use only the standard levels the host logger exposes (typically info, warn, error, debug, trace).
  2. Map fatal -> error and verbose/silly -> debug in the worker-side log proxy.
  3. Inspect the host logger object to confirm which level methods exist before wiring the plugin.

Example fix

// before
log.fatal('disk full')
// after
log.error('disk full')
Defensive patterns

Strategy: type-guard

Validate before calling

const KNOWN_LOG_METHODS = ['info','warn','error','debug','trace'] as const
function isKnownLogMethod(m: string): boolean {
  return (KNOWN_LOG_METHODS as readonly string[]).includes(m)
}

Type guard

function isLogLevel(m: string): m is 'info'|'warn'|'error'|'debug'|'trace' {
  return ['info','warn','error','debug','trace'].includes(m)
}

Try / catch

try { (log as any)[method](msg, fields) }
catch (e) {
  if (e instanceof Error && e.code === 'plugin.capability.unavailable') {
    log.error(msg, fields) // downgrade to a known level
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Plugin calls log.fatal() when the host logger has no fatal method, or any typo / unsupported level such as log.silly(), log.verbose(), log.log().

Common situations: Plugin ported from a logger with extra levels (fatal, silly, verbose); version skew where the host logger was trimmed; typo in the method name; plugin assumes console-style log.log().

Related errors


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