yt-dlp says ffmpeg is not installed — and your video has no sound
Why the download 'worked' but left you two files, one silent. Installing FFmpeg on a Mac, pointing yt-dlp at it, and repairing the files you already have without re-downloading.
The download finishes. It says it worked. Then you open the file and there is no sound — or there are two files where you expected one:
WARNING: You have requested merging of multiple formats but ffmpeg is not
installed. The formats won't be merged
video.f299.mp4 ← the picture, silent
video.f140.m4a ← the sound, separate
That is a warning, not an error. yt-dlp exits successfully and the shell gives you no sign anything went wrong. Which is why people arrive here searching for “downloaded video has no audio” rather than for the message that actually explains it.
Why there were two files in the first place
Above 360p, YouTube does not store video and audio together. It serves a video-only stream and an audio-only stream and expects the player to combine them. That is normal, it is how adaptive streaming works everywhere, and it is not something yt-dlp chose.
So any download better than 360p is two downloads plus a mux: a step that puts both streams into one container. Muxing is not re-encoding — nothing is recompressed, no quality is lost, it takes about a second — but it needs a tool that understands media containers.
That tool is FFmpeg. Without it, yt-dlp fetches both streams correctly, cannot combine them, and leaves them side by side.
You can see the seam in the filenames: .f299 and .f140 are the format IDs
from yt-dlp -F. Any filename with .f<number>. in it is an unmerged part.
Install FFmpeg
With Homebrew:
brew install ffmpeg
Then check that yt-dlp can actually see it:
which ffmpeg
ffmpeg -version | head -1
which printing nothing is the whole problem: FFmpeg is installed somewhere
yt-dlp does not look.
If it is installed but yt-dlp still complains
Two usual causes on a Mac.
Apple Silicon PATH. Homebrew installs to /opt/homebrew/bin on Apple
Silicon and /usr/local/bin on Intel. Shells configured years ago, or copied
from an Intel machine, often only know the second.
echo $PATH | tr ':' '\n' | grep -c '/opt/homebrew/bin'
0 means it is missing. Add it to ~/.zshrc:
eval "$(/opt/homebrew/bin/brew shellenv)"
Then open a new terminal — the current one keeps the old environment.
yt-dlp launched by something else. A cron job, an app, a script or an
automation runs with a minimal PATH that rarely includes Homebrew’s. The
process sees a different environment from your terminal, which is why “it works
when I type it but not when it runs by itself” is such a common report.
Point at it explicitly and skip the whole question:
yt-dlp --ffmpeg-location /opt/homebrew/bin "URL"
That flag takes the containing directory, not the binary. It also accepts
the binary path, but the directory is the form that finds ffprobe next to it,
which yt-dlp also wants.
Permanently, in ~/.config/yt-dlp/config:
--ffmpeg-location /opt/homebrew/bin
Watch for a trap: if the path does not exist, yt-dlp warns and carries on without FFmpeg rather than stopping. A typo here produces exactly the silent file you were trying to fix.
Repair the files you already have
Do not download them again. You have both streams; they only need joining:
ffmpeg -i video.f299.mp4 -i video.f140.m4a \
-c copy video.mp4
-c copy is the important part: stream copy, no re-encoding. It runs in
about a second on a large file and loses nothing. If you find yourself waiting
minutes, you left -c copy out and are recompressing a file that did not need
it.
Mismatched container and codec — say a VP9 .webm picture with an .m4a
sound — go into MKV, which accepts any combination:
ffmpeg -i video.f303.webm -i video.f140.m4a -c copy video.mkv
Or let yt-dlp redo the merge from the parts it kept:
yt-dlp --load-info-json video.info.json ...
only if you passed --write-info-json at the time. Usually the ffmpeg line
above is quicker.
Avoiding it entirely
If you never want a merge step, ask for a format that is already one file:
yt-dlp -f "best[ext=mp4]" "URL"
Be clear about the cost: pre-merged formats top out at 360p, because that is the last resolution YouTube still serves combined. It is the right answer for a quick reference clip and the wrong one for anything you intend to watch.
The better trade, if QuickTime compatibility is what you are after, is 1080p H.264 — still needs the merge, but plays everywhere afterwards:
yt-dlp -f "bestvideo[vcodec^=avc1]+bestaudio[ext=m4a]" "URL"
There is more on that choice in why your 4K download won’t open in QuickTime.
One note on FFmpeg builds
Not every FFmpeg is the same FFmpeg. Some builds are compiled without the
encoders you expect, and some static builds circulating online are compiled
--enable-nonfree, which makes them not legally redistributable — fine to
use yourself, a licensing problem the moment you ship them inside something.
Homebrew’s build is the sane default on a Mac.
This is also why the app I build does not bundle FFmpeg: it fetches a properly licensed build on first launch, checks it against a published SHA-256 and a Developer ID signature, and refuses it otherwise. Same reasoning, moved off your desk — and it means the silent-file version of this problem cannot happen there, because the app will not start a merge it cannot finish.
The short version
| Symptom | Cause | Fix |
|---|---|---|
| Two files, one silent | No FFmpeg | brew install ffmpeg |
which ffmpeg prints nothing |
Not on PATH |
eval "$(/opt/homebrew/bin/brew shellenv)" |
| Works in terminal, not from a script | Different PATH |
--ffmpeg-location /opt/homebrew/bin |
| Already have the two parts | — | ffmpeg -i a -i b -c copy out.mp4 |
| Never want a merge | — | -f "best[ext=mp4]", capped at 360p |