agalwood/Motrix · error · BridgeReceiverError
unsupported-kind
unsupported-kind
Error message
ffmpeg unavailable — mux pipeline not active
What it means
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.
Source
Thrown at src/core/bridge-receiver/bridge-receiver.ts:364
// mux pair the direct submit is transparently re-routed to MuxPipeline.
// resolveToMux owns its own error handling (catches → null), so we never
// wrap this in try/catch here — a null means "proceed as direct".
//
// Cookie handoff (bilibili HD): when the extension submits a direct download
// with cookies (e.g. SESSDATA for bilibili), serialize them into a Cookie
// header string and pass it as the 2nd arg to resolveToMux. The resolver
// attaches the header ONLY to api.bilibili.com calls — never to CDN URLs.
if (adapted.kind === 'direct' && this.deps.resolveToMux) {
const rawCookies =
params.selection.kind === 'direct'
? params.selection.primary.cookies
: []
const serialized = serializeCookieHeader(rawCookies)
const cookieHeader = serialized || undefined
const m = await this.deps.resolveToMux(adapted.primaryUrl, cookieHeader)
if (m) {
if (!this.mux) {
throw new BridgeReceiverError(
'unsupported-kind',
'ffmpeg unavailable — mux pipeline not active'
)
}
// Prefer the resolver's human title over the URL-derived name (a bvid
// like BV1xxx is meaningless). Sanitize, append the container
// extension, THEN dedup — the pick must run on the on-disk name.
const titleBase = m.title ? sanitizeFilename(m.title).trim() : ''
const finalName = titleBase
? await this.deps.pickName(
adapted.saveDir,
ensureMediaExtension(titleBase, m.container)
)
: adapted.finalName
const muxAdapted: AdaptedMux = {
kind: 'mux',
taskId: adapted.taskId,
saveDir: adapted.saveDir,View on GitHub (pinned to 1a708ee577)
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
Example fix
// before
const receiver = new BridgeReceiver({ ...deps, ffmpegBinaryPath: null, resolveToMux: bilibiliResolver })
// after
const receiver = new BridgeReceiver({ ...deps, ffmpegBinaryPath: '/usr/bin/ffmpeg', resolveToMux: bilibiliResolver }) Defensive patterns
Strategy: validation
Validate before calling
if (!receiverDeps.ffmpegBinaryPath && receiverDeps.resolveToMux) {
// ffmpeg absent but mux resolution is wired — direct submits that resolve
// to mux pairs will throw. Either remove resolveToMux or set ffmpegBinaryPath.
throw new Error('ffmpegBinaryPath is required when resolveToMux is provided')
} Try / catch
try {
await receiver.handleSubmit(params)
} catch (e) {
if (e instanceof BridgeReceiverError && e.code === 'unsupported-kind') {
// ffmpeg not available — degrade to direct download or prompt user to install ffmpeg
await promptInstallFfmpeg()
} else throw e
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- invalid-url-scheme
- plugin.command.not_declared_in_manifest
- plugin.http.redirect_not_allowed
- PluginRuntimeFault
- plugin.ffmpeg.destination_phase_disallowed
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/f27d3a19f7b4f584.
Report an issue: GitHub.