{"record":{"id":"f27d3a19f7b4f584","repo":"agalwood/Motrix","slug":"unsupported-kind","errorCode":"unsupported-kind","errorMessage":"ffmpeg unavailable — mux pipeline not active","messagePattern":"ffmpeg unavailable — mux pipeline not active","errorType":"error_code","errorClass":"BridgeReceiverError","httpStatus":null,"severity":"error","filePath":"src/core/bridge-receiver/bridge-receiver.ts","lineNumber":364,"sourceCode":"    // mux pair the direct submit is transparently re-routed to MuxPipeline.\n    // resolveToMux owns its own error handling (catches → null), so we never\n    // wrap this in try/catch here — a null means \"proceed as direct\".\n    //\n    // Cookie handoff (bilibili HD): when the extension submits a direct download\n    // with cookies (e.g. SESSDATA for bilibili), serialize them into a Cookie\n    // header string and pass it as the 2nd arg to resolveToMux. The resolver\n    // attaches the header ONLY to api.bilibili.com calls — never to CDN URLs.\n    if (adapted.kind === 'direct' && this.deps.resolveToMux) {\n      const rawCookies =\n        params.selection.kind === 'direct'\n          ? params.selection.primary.cookies\n          : []\n      const serialized = serializeCookieHeader(rawCookies)\n      const cookieHeader = serialized || undefined\n      const m = await this.deps.resolveToMux(adapted.primaryUrl, cookieHeader)\n      if (m) {\n        if (!this.mux) {\n          throw new BridgeReceiverError(\n            'unsupported-kind',\n            'ffmpeg unavailable — mux pipeline not active'\n          )\n        }\n        // Prefer the resolver's human title over the URL-derived name (a bvid\n        // like BV1xxx is meaningless). Sanitize, append the container\n        // extension, THEN dedup — the pick must run on the on-disk name.\n        const titleBase = m.title ? sanitizeFilename(m.title).trim() : ''\n        const finalName = titleBase\n          ? await this.deps.pickName(\n              adapted.saveDir,\n              ensureMediaExtension(titleBase, m.container)\n            )\n          : adapted.finalName\n        const muxAdapted: AdaptedMux = {\n          kind: 'mux',\n          taskId: adapted.taskId,\n          saveDir: adapted.saveDir,","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/bridge-receiver/bridge-receiver.ts#L346-L382","documentation":"Thrown by BridgeReceiver when a direct download's URL is resolved to a mux pair (separate video+audio streams, e.g. bilibili HD) via resolveToMux, but the MuxPipeline was never constructed because ffmpegBinaryPath was null at receiver construction time. The code path transparently re-routes direct submits to the mux pipeline when resolveToMux returns non-null, so this fires specifically when that re-routing succeeds but the mux pipeline itself is absent. Code 'unsupported-kind' is the wire contract the browser extension branches on.","triggerScenarios":"A browser extension submits a direct download for a bilibili (or similar) URL with cookies; resolveToMux succeeds and returns a {videoUrl, audioUrl} pair; this.mux is null because BridgeReceiverDeps.ffmpegBinaryPath was null when the receiver was constructed.","commonSituations":"Headless/Node-only shell (no ffmpeg bundled), ffmpeg binary path misconfigured or pointing at a nonexistent file, or a test harness that constructs BridgeReceiver with ffmpegBinaryPath:null but submits a URL that the resolver upgrades to mux. End users on a fresh install where ffmpeg has not yet been downloaded or located.","solutions":["Set ffmpegBinaryPath to a valid ffmpeg binary path in BridgeReceiverDeps before constructing the receiver","Verify ffmpeg is installed and discoverable — run the ffmpeg detection/dependency-resolution step at app startup so the path resolves","If running in a restricted shell without ffmpeg, prevent resolveToMux from being wired (omit the deps.resolveToMux factory) so direct submits fall through to DirectPipeline instead of being upgraded","In tests, pass a real or stubbed ffmpegBinaryPath and a resolveToMux stub that returns null to exercise the direct path"],"exampleFix":"// before\nconst receiver = new BridgeReceiver({ ...deps, ffmpegBinaryPath: null, resolveToMux: bilibiliResolver })\n// after\nconst receiver = new BridgeReceiver({ ...deps, ffmpegBinaryPath: '/usr/bin/ffmpeg', resolveToMux: bilibiliResolver })","handlingStrategy":"validation","validationCode":"if (!receiverDeps.ffmpegBinaryPath && receiverDeps.resolveToMux) {\n  // ffmpeg absent but mux resolution is wired — direct submits that resolve\n  // to mux pairs will throw. Either remove resolveToMux or set ffmpegBinaryPath.\n  throw new Error('ffmpegBinaryPath is required when resolveToMux is provided')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await receiver.handleSubmit(params)\n} catch (e) {\n  if (e instanceof BridgeReceiverError && e.code === 'unsupported-kind') {\n    // ffmpeg not available — degrade to direct download or prompt user to install ffmpeg\n    await promptInstallFfmpeg()\n  } else throw e\n}","preventionTips":["Validate that ffmpegBinaryPath is set at startup before allowing mux-resolving submits","Surface a user-facing warning in the UI when ffmpeg is absent and disable HD/mux capture options"],"tags":["ffmpeg","bridge-receiver","mux","configuration"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}