{"record":{"id":"24e693153bc8a1a6","repo":"agalwood/Motrix","slug":"plugin-ffmpeg-op-not-found","errorCode":"plugin.ffmpeg.op_not_found","errorMessage":"ffmpeg op not found: ${opId}","messagePattern":"ffmpeg op not found: (.+?)","errorType":"exception","errorClass":"PluginCodedError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/host/capability-bridge.ts","lineNumber":1114,"sourceCode":"        'ffmpeg saveDir write in beforeFinalize requires a FfmpegStaging in HookContextArgs'\n      )\n    }\n    const staged = this.hookStaging.redirectOutput(userOutput)\n    // ensureDir must precede assertQuota: assertQuota does a readdir of the staging dir.\n    await this.hookStaging.ensureDir()\n    await this.hookStaging.assertQuota()\n    return staged\n  }\n\n  private async dispatchFfmpeg(msg: BridgeCallMessage): Promise<unknown> {\n    const ffmpeg = this.opts.capabilityHost.ffmpeg\n\n    // ── op lifecycle methods ───────────────────────────────────────────────\n    if (msg.method === 'op.result.await') {\n      const [opId] = ffmpegOpIdSchema.parse(msg.args)\n      const entry = this.ffmpegOps.get(opId)\n      if (!entry) {\n        throw new PluginCodedError(\n          'plugin.ffmpeg.op_not_found',\n          `ffmpeg op not found: ${opId}`\n        )\n      }\n      try {\n        const result = await entry.handle.result\n        this.ffmpegOps.delete(opId)\n        return result\n      } catch (e: unknown) {\n        this.ffmpegOps.delete(opId)\n        throw e\n      }\n    }\n\n    if (msg.method === 'op.progress.pull') {\n      const [opId] = ffmpegOpIdSchema.parse(msg.args)\n      const entry = this.ffmpegOps.get(opId)\n      if (!entry) {","sourceCodeStart":1096,"sourceCodeEnd":1132,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/host/capability-bridge.ts#L1096-L1132","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Capture the opId only from the launch method's { opId } return value.","Await each op exactly once — the entry is removed after the result settles.","Do not cache or reuse opIds across plugin reactivations or bridge restarts."],"exampleFix":"// before — awaiting a hardcoded/stale id\nconst r = await ffmpegOp.result('op-1')\n// after\nconst { opId } = await ffmpeg.transcode(opts)\nconst r = await ffmpegOp.result(opId)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// track only opIds you actually launched; treat { opId } as single-use\nconst liveOps = new Set<string>()\nasync function launch(opts: FfmpegRunOpts): Promise<string> {\n  const { opId } = await ffmpeg.run(opts)\n  liveOps.add(opId)\n  return opId\n}\nfunction isLiveOp(opId: string): boolean {\n  return liveOps.has(opId)\n}","tryCatchPattern":"try {\n  const result = await ffmpegOp.result(opId)\n} catch (e) {\n  if ((e as { code?: string }).code === 'plugin.ffmpeg.op_not_found') {\n    // op was already consumed or is unknown — nothing to await; stop\n  } else {\n    throw e\n  }\n}","preventionTips":["Capture opId only from the launch method's { opId } return.","Await each op exactly once; delete it from your own set when done.","Never reuse opIds across bridge/plugin restarts."],"tags":["plugin","ffmpeg","op-handle","lifecycle"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}