Stream map matches no streams" means no audio, not a bad file

Your batch has run clean for weeks. Then one screen recording lands in the queue and FFmpeg stops with Stream map '0:a' matches no streams, exit code 1, no output file written. The file isn't corrupt and your command isn't malformed. The clip simply has no audio track, and you told FFmpeg that audio was mandatory.
Quick answer: A stream map matches no streams error means FFmpeg was told to map an audio (or video) stream that doesn't exist in the input, which is normal for screen recordings, GoPro clips, and AI-generated video. The direct fix is the optional specifier:ffmpeg -i in.mp4 -map 0:v:0 -map '0:a?' -c copy out.mp4. For a pipeline that handles mixed uploads, probe first withffprobe -select_streams aand inject a silentanullsrctrack when downstream steps need every file to have audio. FFmpeg Micro does that probe-and-normalize step inside one API call, so you skip the branching, the encoder hosting, and the FFmpeg install entirely.
A missing audio track is a hard failure, not a warning
FFmpeg treats -map as a contract. When you write -map 0:a, you're saying "input 0 has audio, use it," and if the demuxer finds no audio stream, FFmpeg aborts before writing a single byte. That's deliberate: silently dropping a track you explicitly asked for would be worse than stopping.
The common belief is that this error means something is wrong with the file. Usually nothing is. The files that trigger it are the ones your users actually upload: OBS and Loom screen captures recorded with the mic off, GoPro clips shot in a housing with audio disabled, Runway and Sora output (text-to-video models return silent MP4s), Canva and CapCut exports where the music track was removed, and anything that has already been through a -an pass earlier in your own pipeline.
The bug reports back this up. FFmpeg's own tracker has an open ticket for this exact string filed in May 2024 (trac #10997), and the same message shows up as a user-facing crash in downstream tools: h265ize issue #27, streamlink discussion #5512, and kkroening/ffmpeg-python issue #204, which is titled "Keeping track of audio stream when none exists." Every one of those is a wrapper author discovering that a hardcoded audio map doesn't survive real input.
The question mark makes a stream mapping optional
Appending ? to a stream specifier tells FFmpeg to use the stream if it exists and move on quietly if it doesn't. It's documented behavior, not a hack.
# Fails on any silent input
ffmpeg -i input.mp4 -map 0:v:0 -map 0:a -c copy output.mp4
# Works on both: audio is copied when present, skipped when absent
ffmpeg -i input.mp4 -map 0:v:0 -map '0:a?' -c copy output.mp4
Quote the specifier. In zsh (the default shell on macOS since Catalina), an unquoted ? is a glob character, and you'll get zsh: no matches found: 0:a? before FFmpeg ever runs. That error looks nothing like an FFmpeg problem, which is why it eats twenty minutes the first time.
The optional specifier works anywhere a stream specifier does: -map '0:a:1?' for a second audio track that may not be there, -map '0:s?' for subtitles, -c:a:1? for a codec on a stream that might not exist.
Here's the part that matters more than the fix: -map '0:a?' doesn't solve your pipeline, it just moves the failure downstream. You now produce a video-only MP4, and the next step (a concat, a platform upload, an audio mix) breaks somewhere less obvious, with a worse error message. The hard failure was doing you a favor by surfacing the mismatch at the earliest possible point.
`-map 0:v` and `-map 0:v:0` select different things
-map 0:v selects every video stream in input 0, while -map 0:v:0 selects only the first one. On a plain MP4 they're identical, which is why the difference stays invisible until it doesn't.
Files that carry more than one "video" stream are more common than they look. An MP3 or M4A with cover art exposes the artwork as an mjpeg stream flagged attached_pic, so -map 0:v grabs it and your libx264 encode either fails or emits a one-frame video. GoPro MP4s carry tmcd timecode and gpmd telemetry streams alongside the picture. Some phone exports include a low-resolution preview track.
Use -map 0:v:0 -map '0:a?' as the default pair for single-track output. It's explicit about the video you want and forgiving about the audio you may not have.
Probe for audio before you build the command
One ffprobe call tells you whether a file has an audio stream, and it's cheap enough to run on every upload. Filter to audio streams and print nothing else:
ffprobe -v error -select_streams a \
-show_entries stream=codec_name,channels,sample_rate \
-of csv=p=0 input.mp4
A file with sound prints something like aac,2,48000. A silent file prints an empty string and exits 0. That empty-versus-nonempty check is the entire branch condition, and it works the same in a Bash script, an n8n Function node, or a Make router filter.
For structured pipelines, -of json gives you a streams array you can test for length. This is the same validate-on-ingest habit that catches truncated uploads, which we covered in FFmpeg Invalid Data Found Isn't a Corrupt Video: probe once at the door, and every later step gets to assume a known shape.
Silent audio is what makes a mixed batch concat-safe
When you're joining clips, optional mapping isn't enough. The concat filter with a=1 requires an audio stream from every input, and the concat demuxer requires all files to have the same stream layout. One silent clip in a set of ten and you get Stream specifier ':a' in filtergraph description ... matches no streams, or worse, a join that succeeds and drops audio for the whole tail of the output.
The fix is to give the silent file a real, empty audio track so all inputs match:
ffmpeg -i silent.mp4 \
-f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 \
-map 0:v:0 -map 1:a -c:v copy -c:a aac -b:a 128k -shortest \
normalized.mp4
anullsrc generates silence at whatever layout and rate you specify. aevalsrc=0:channel_layout=stereo:sample_rate=48000 does the same thing through the expression evaluator if you prefer it. -shortest is what stops the infinite silence generator from producing an infinitely long file, and forgetting it is the single most common way this command hangs.
Match the sample rate and channel layout to the rest of your batch, not to whatever the first file happens to use. Mixed audio parameters across a concat cause the same class of breakage as mixed resolutions, which is the normalize-first rule from FFmpeg Concat Different Resolutions. Scale and pad to one canvas, force a constant frame rate, force one audio layout, then join.
If you'd rather not maintain this branch at all, this is exactly what FFmpeg Micro is for: submit heterogeneous uploads as a job, and the normalize step (probe, pad, silent-track injection, uniform encode) happens server-side with no binaries to install and no long-running process to babysit. The API docs show the job submit, poll, and download flow, and it's callable from code, from n8n, Make, and Zapier, or from your AI agents over MCP.
The exit code won't tell you which failure you hit
FFmpeg exits 1 for a missing stream map, and it also exits 1 for a missing input file, an unknown encoder, and a full disk. Branching on the exit code tells you that something failed, never what to do about it.
The distinction you actually need is retryable versus not. A stream map error is deterministic: the same file with the same command fails identically forever, so retrying it is wasted compute and a stuck queue. Parse stderr for the string and route it to a normalize path instead. We laid out the full triage approach in FFmpeg Error Messages, Not Exit Codes, Tell You What to Retry.
Capture stderr on every invocation. If you're shelling out from Node or Python and only checking the return code, you're throwing away the only diagnostic FFmpeg gives you.
Hand-written branching versus one normalize job
Both approaches produce the same output. What differs is how much of the branching logic you own.
| Step | Self-hosted FFmpeg | FFmpeg Micro |
|---|---|---|
| Detect missing audio | `ffprobe -select_streams a`, parse output | Handled inside the job |
| Branch the command | if/else in Bash, n8n Function node, or Make router | None to write |
| Inject silent track | Second `anullsrc` input plus `-shortest` | Handled inside the job |
| Keep FFmpeg current | Your build, your `?`-support, your CVE patching | Managed |
| Long renders | Worker timeouts, memory limits, retry queue | Submit, poll or webhook, download |
| Cost to start | Server time plus your time | Free tier |
The self-hosted path is fine when you control the inputs. It gets expensive the moment your inputs come from users, because every new source format adds another branch to code you didn't want to own.
Common pitfalls
Six pitfalls cost the most time here, roughly in order of how often they bite.
- Leaving
?unquoted in zsh, which fails as a glob before FFmpeg starts. - Adding
-map '0:a?'and calling it done, then hitting a broken concat two steps later because half the batch is now video-only. - Omitting
-shortestwithanullsrc, producing a job that never terminates and eventually gets killed by your worker timeout. - Using
-map 0:von files with cover art or GoPro telemetry, and encoding the wrong stream. - Generating silence at 44100 Hz mono when the rest of the batch is 48000 Hz stereo, which pushes the failure into the concat step.
- Treating exit code 1 as retryable and looping a deterministic failure through your queue.
FAQ
What does "Stream map '0:a' matches no streams" mean?
The error means FFmpeg was told to include an audio stream from the first input, and that input contains no audio stream at all. It's a command-versus-file mismatch, not file corruption, and it stops the job before any output is written.
How do I make FFmpeg ignore a missing audio track?
Add a question mark to the stream specifier: -map '0:a?' copies audio when it exists and skips the mapping when it doesn't. Quote the specifier so your shell doesn't interpret ? as a glob.
How do I check if a video has an audio track?
Run ffprobe -v error -select_streams a -show_entries stream=codec_name -of csv=p=0 input.mp4. It prints a codec name for each audio stream and prints nothing for a silent file, so an empty result is your branch condition.
How do I add a silent audio track to a video with FFmpeg?
Add anullsrc as a second lavfi input, map video from the file and audio from the generator, and cap the duration: ffmpeg -i in.mp4 -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 -map 0:v:0 -map 1:a -c:v copy -c:a aac -shortest out.mp4. Match the sample rate and channel layout to the rest of your batch.
Why does my concat fail even after I added -map 0:a?
The optional specifier only relaxes the mapping for that one command, so it produces a video-only file that a concat with a=1 still rejects. Normalize every clip to the same stream layout first, injecting silent audio where it's missing, then join.
Once the branching logic is written, the part that stays annoying is the infrastructure around it: an encoder to keep patched, a worker that survives a fifteen-minute render, and a retry policy that knows a stream map error is never worth retrying. That's the part you can hand off. Sign up free and send your next mixed-upload batch as one API call.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

FFmpeg Invalid Data Found Isn't a Corrupt Video. Validate Input.
FFmpeg invalid data found when processing input rarely means a corrupt video. The real causes on files you didn't create, plus a copy-paste ffprobe guard.

FFmpeg Error Messages, Not Exit Codes, Tell You What to Retry
FFmpeg error messages, not exit codes, tell you what broke: a triage table of exact stderr strings, the cause in a pipeline, the fix, and retry or reject.

Non-monotonous DTS Isn't Noise. It's Truncating Your Joins.
Non-monotonous DTS in output stream: what the warning means, when -fflags +genpts or -reset_timestamps 1 is right, and when only a CFR re-encode fixes it.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free