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
- 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.
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
- 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.
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
- plugin.lifecycle.secrets_seed_missing
- plugin.lifecycle.deactivate_timeout
- PluginRuntimeFault
- PluginActivationCapExceeded
- plugin.fs.task.not_available_outside_hook
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/24e693153bc8a1a6.
Report an issue: GitHub.