{"record":{"id":"8ecbe8221124de24","repo":"agalwood/Motrix","slug":"plugin-capability-bad-args","errorCode":"plugin.capability.bad_args","errorMessage":"log.${msg.method}: invalid args","messagePattern":"log\\.(.+?): invalid args","errorType":"exception","errorClass":"PluginCodedError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/host/capability-bridge.ts","lineNumber":597,"sourceCode":"   * always permitted (auto-injected). When `effectivePermissions` is\n   * undefined, all capabilities are permitted (back-compat for tests).\n   */\n  private permitted(capability: string): boolean {\n    const required = CAPABILITY_PERMISSIONS[capability]\n    if (!required) return true\n    const eff = this.opts.effectivePermissions\n    if (!eff) return true\n    return required.some((p) => eff.has(p))\n  }\n\n  // -------------------------------------------------------------------------\n  // log\n  // -------------------------------------------------------------------------\n\n  private dispatchLog(msg: BridgeCallMessage): void {\n    const parsed = logArgsSchema.safeParse(msg.args)\n    if (!parsed.success) {\n      throw new PluginCodedError(\n        'plugin.capability.bad_args',\n        `log.${msg.method}: invalid args`\n      )\n    }\n    const [text, ...rest] = parsed.data\n    const fields = rest[0] as Record<string, unknown> | undefined\n    const fn = (\n      this.log as unknown as Record<string, (m: string, f?: object) => void>\n    )[msg.method]\n    if (typeof fn !== 'function') {\n      throw new PluginCodedError(\n        'plugin.capability.unavailable',\n        `unknown log method: ${msg.method}`\n      )\n    }\n    fn.call(this.log, String(text), fields)\n  }\n","sourceCodeStart":579,"sourceCodeEnd":615,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/host/capability-bridge.ts#L579-L615","documentation":"Thrown by CapabilityBridge.dispatchLog when logArgsSchema.safeParse(msg.args) fails. logArgsSchema is z.tuple([z.string()]).rest(z.unknown()), so the first argument MUST be a string. Tagged plugin.capability.bad_args and marshaled back to the plugin VM as a coded error.","triggerScenarios":"A plugin calls log.info(/warn/error/debug/trace) with a non-string first argument — e.g. log.info({code:1}), log.info(42), log.info(undefined). The safeParse at capability-bridge.ts:595 fails and the throw at 597-600 fires.","commonSituations":"Plugin passes a fields object as the first arg instead of a message string; worker-side log proxy forwards args in the wrong order; plugin assumes log accepts an object-only call like pino/winston; arity mismatch after a bridge refactor.","solutions":["Make the first log argument a string message: log.info('download started', { bytes }).","Pass structured fields only as the optional second argument.","If migrating from a logger that accepts object-first calls, add a shim in the worker proxy that swaps the args."],"exampleFix":"// before\nlog.info({ bytes: 1024 })\n// after\nlog.info('chunk received', { bytes: 1024 })","handlingStrategy":"validation","validationCode":"import { z } from 'zod'\nconst logArgsSchema = z.tuple([z.string()]).rest(z.unknown())\n// Validate before forwarding to the bridge.\nif (!logArgsSchema.safeParse(args).success) {\n  args = [String(args[0] ?? '')].concat(args.slice(1))\n}","typeGuard":"function isLogCall(args: unknown[]): args is [string, ...unknown[]] {\n  return typeof args[0] === 'string'\n}","tryCatchPattern":"try { log.info(...args) }\ncatch (e) {\n  if (e instanceof Error && e.code === 'plugin.capability.bad_args') {\n    log.info(String(args[0] ?? ''), args[1]) // coerce first arg to string\n    return\n  }\n  throw e\n}","preventionTips":["In the worker-side log proxy, coerce the first arg with String(...) before posting the bridge message.","Treat log as (message: string, fields?: object) and document it in the plugin typings.","Add a unit test that log.info(object) throws bad_args to lock the contract."],"tags":["plugin","capability","log","validation"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}