agalwood/Motrix · error · PluginCodedError

plugin.ffmpeg.op_not_found

plugin.ffmpeg.op_not_found

Error message

ffmpeg op not found: ${opId}

What it means

ffmpeg.op.result.await(opId) looks the op up in the per-bridge ffmpegOps map; a miss throws plugin.ffmpeg.op_not_found. The opId was never registered (not returned by a launch method), already consumed, or belongs to a different bridge instance.

Source

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

        'ffmpeg saveDir write in beforeFinalize requires a FfmpegStaging in HookContextArgs'
      )
    }
    const staged = this.hookStaging.redirectOutput(userOutput)
    // ensureDir must precede assertQuota: assertQuota does a readdir of the staging dir.
    await this.hookStaging.ensureDir()
    await this.hookStaging.assertQuota()
    return staged
  }

  private async dispatchFfmpeg(msg: BridgeCallMessage): Promise<unknown> {
    const ffmpeg = this.opts.capabilityHost.ffmpeg

    // ── op lifecycle methods ───────────────────────────────────────────────
    if (msg.method === 'op.result.await') {
      const [opId] = ffmpegOpIdSchema.parse(msg.args)
      const entry = this.ffmpegOps.get(opId)
      if (!entry) {
        throw new PluginCodedError(
          'plugin.ffmpeg.op_not_found',
          `ffmpeg op not found: ${opId}`
        )
      }
      try {
        const result = await entry.handle.result
        this.ffmpegOps.delete(opId)
        return result
      } catch (e: unknown) {
        this.ffmpegOps.delete(opId)
        throw e
      }
    }

    if (msg.method === 'op.progress.pull') {
      const [opId] = ffmpegOpIdSchema.parse(msg.args)
      const entry = this.ffmpegOps.get(opId)
      if (!entry) {

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Capture the opId only from the launch method's { opId } return value.
  2. Await each op exactly once — the entry is removed after the result settles.
  3. Do not cache or reuse opIds across plugin reactivations or bridge restarts.

Example fix

// before — awaiting a hardcoded/stale id
const r = await ffmpegOp.result('op-1')
// after
const { opId } = await ffmpeg.transcode(opts)
const r = await ffmpegOp.result(opId)
Defensive patterns

Strategy: type-guard

Type guard

// track only opIds you actually launched; treat { opId } as single-use
const liveOps = new Set<string>()
async function launch(opts: FfmpegRunOpts): Promise<string> {
  const { opId } = await ffmpeg.run(opts)
  liveOps.add(opId)
  return opId
}
function isLiveOp(opId: string): boolean {
  return liveOps.has(opId)
}

Try / catch

try {
  const result = await ffmpegOp.result(opId)
} catch (e) {
  if ((e as { code?: string }).code === 'plugin.ffmpeg.op_not_found') {
    // op was already consumed or is unknown — nothing to await; stop
  } else {
    throw e
  }
}

Prevention

When it happens

Trigger: Plugin awaits an opId it fabricated, one from a previous bridge instance, or one already consumed — the entry is deleted from ffmpegOps after the result resolves or rejects.

Common situations: Calling result(opId) twice on the same op; awaiting an opId after the worker/plugin was reactivated; passing a plain string that is not an opId.

Related errors


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