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

  1. Set ffmpegBinaryPath to a valid ffmpeg binary path in BridgeReceiverDeps before constructing the receiver
  2. Verify ffmpeg is installed and discoverable — run the ffmpeg detection/dependency-resolution step at app startup so the path resolves
  3. 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
  4. 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

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


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