ffmpegvideo-processingdebugging

FFmpeg Invalid Data Found Isn't a Corrupt Video. Validate Input.

·Javid Jamae·10 min read
FFmpeg Invalid Data Found Isn't a Corrupt Video. Validate Input.

Your pipeline processed four hundred uploads today and eleven died on the same line: Invalid data found when processing input. Pull one of the failing files down, double-click it, and QuickTime plays it fine. The file isn't broken, so whatever FFmpeg received wasn't the file.

Quick answer: FFmpeg's "Invalid data found when processing input" almost never means a corrupt video. It means the bytes at that path aren't a container libavformat can probe, usually an HTML or XML error page saved with an .mp4 extension, a zero-byte temp file, a truncated download, or a pipe that closed before anything was written. Run ffprobe -v error -show_entries format=format_name,duration -of json FILE on every input first and reject anything that exits non-zero or reports zero duration. Or hand the URL to FFmpeg Micro and let probe-then-process run as one API call with no encoder to host.

What FFmpeg is actually telling you

FFmpeg raises this error from avformat_open_input, which reads the head of the file, scores it against every demuxer it has, and returns AVERROR_INVALIDDATA when nothing scores high enough. It reads up to 5,000,000 bytes or 5 seconds by default. The string is identical for an HTML page, a 0-byte file, or a real MP4 with its header sheared off, which is why it's useless on its own.

FFmpeg ignores the input file extension and probes content, so a WebM named clip.mp4 opens without complaint. Wrong extension is only a cause when you force a demuxer with -f, or when your storage layer named an error page .mp4 and you believed it.

The position of the error tells you which bug you have

Where the line lands in stderr splits this into two different bugs. Before any Input #0, ... banner, the probe failed and you never had a media file. After the stream mapping, as Error while decoding stream #0:0: Invalid data found when processing input, the container opened and one packet failed to decode, usually a truncated file or a stream the decoder can't handle. Open-time failures are ingest bugs. Decode-time failures are content bugs. Sorting alerts by that distinction kills most of the noise.

Reproduce all five causes in under a minute

Reproduce each cause locally once. The stderr from a deliberate failure is what you'll pattern-match in production at 2am.

The signed URL returned an error page. Top cause on any service that fetches user media from S3, Cloudflare R2, or a customer's bucket. An expired presigned URL returns HTTP 403 with a ~250-byte XML body, and curl -o clip.mp4 writes it to disk under an innocent name:

printf '<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code></Error>' > clip.mp4
ffmpeg -i clip.mp4 -f null -
# clip.mp4: Invalid data found when processing input

curl without -f exits 0 here. Your job "succeeded" at the download step.

The temp file is empty or half-written. A killed encoder, an interrupted S3 multipart upload, or a worker that reads a shared volume when the file appears rather than when the writer closes it:

: > empty.mp4
ffmpeg -i empty.mp4 -f null -

head -c 4096 real.mp4 > partial.mp4
ffmpeg -i partial.mp4 -f null -
# [mov,mp4,m4a,3gp,3g2,mj2] moov atom not found
# partial.mp4: Invalid data found when processing input

A truncated MP4 usually announces itself with moov atom not found on the line above, a separate error covered in Fix "moov atom not found" on Uploaded Videos in Your Pipeline.

The stdin pipe closed before anything was written. The ffmpy issue tracker (Ch00k/ffmpy #15) has this variant: the wrapper built a pipe:0 command and the producing process wrote nothing:

ffmpeg -i pipe:0 -c copy out.mp4 < /dev/null
# pipe:0: Invalid data found when processing input

If pipe:0: is the subject of the error, stop debugging FFmpeg. The bug is upstream, in whatever was supposed to write to that pipe.

You forced a demuxer that doesn't match the bytes. Extension mismatches are harmless until someone adds -f:

ffmpeg -f mp4 -i actually_a_webm.mp4 -f null -
# actually_a_webm.mp4: Invalid data found when processing input

Drop the -f on input and let FFmpeg probe. Keep it on output, where the extension genuinely selects the muxer.

The container is fine and the bytes inside aren't. For MPEG-TS and RTSP camera feeds, the default 5 MB probe window can land inside padding or a mid-GOP position and find no usable stream. Frigate's discussion #18796 is this shape: valid stream, failed ingest. The fix isn't rejection, it's a bigger window:

ffmpeg -probesize 50M -analyzeduration 100M -i stream.ts -c copy fixed.mp4

yt-dlp issue #8641 documents another shape: downloads of live and just-ended YouTube streams can carry two ftyp boxes from concatenated fMP4 initialization segments. FFmpeg reads the first, hits the second where it expects media, and calls it invalid data. Remux anything a downloader produced before you trust it.

The ingest guard that stops all of this

The fix is not a better FFmpeg command. It's refusing to hand FFmpeg anything you haven't verified, and returning a rejection reason your support team can read. Four checks, in order of cost:

  1. Compare bytes received to Content-Length. A short read is a truncated file, and you know it before you touch a decoder.
  2. Check the MIME type of the actual bytes. file --mime-type -b input.mp4 returns text/html for an expired signed URL, text/xml for an S3 AccessDenied body, and inode/x-empty for a zero-byte file, in about a millisecond.
  3. Probe format and duration. ffprobe exits non-zero on anything it can't open, and a zero or missing duration catches files that parse but contain nothing.
  4. Assert a video stream with a codec you can decode. A container that holds only a data track will pass step 3 and fail your actual job.

Steps 1 and 2 in bash:

URL="https://your-bucket.s3.amazonaws.com/uploads/clip.mp4?X-Amz-Signature=..."
expected=$(curl -sI "$URL" | awk 'tolower($1)=="content-length:"{print $2+0}')
curl -sf -o input.mp4 "$URL" || { echo "fetch failed"; exit 1; }
actual=$(wc -c < input.mp4)
[ "$expected" = "$actual" ] || { echo "short read: $actual of $expected bytes"; exit 1; }
file --mime-type -b input.mp4

The -f on that second curl matters. Without it, curl writes the 403 body to disk and exits 0.

Steps 3 and 4 in Python, where most of these pipelines live:

import json, subprocess

ALLOWED = {"mov,mp4,m4a,3gp,3g2,mj2", "matroska,webm", "avi", "mpegts"}

def validate(path):
    p = subprocess.run(
        ["ffprobe", "-v", "error", "-show_entries",
         "format=format_name,duration,size", "-show_streams",
         "-of", "json", path],
        capture_output=True, text=True,
    )
    if p.returncode != 0:
        raise ValueError(f"unreadable input: {p.stderr.strip().splitlines()[-1]}")

    data = json.loads(p.stdout)
    fmt = data["format"]
    if fmt["format_name"] not in ALLOWED:
        raise ValueError(f"unsupported container: {fmt['format_name']}")
    if float(fmt.get("duration", 0)) <= 0:
        raise ValueError("file has zero duration")
    if not any(s["codec_type"] == "video" for s in data.get("streams", [])):
        raise ValueError("no video stream present")
    return fmt

format_name for MP4 is the full comma-joined string mov,mp4,m4a,3gp,3g2,mj2, not mp4. FFmpeg in Python covers the subprocess patterns underneath this.

You can also probe without downloading the whole file, worth it on uploads of hundreds of megabytes:

curl -s -r 0-1048575 "$URL" | ffprobe -v error \
  -show_entries format=format_name -of default=nw=1 -i pipe:0

The catch: an MP4 written without -movflags +faststart keeps its moov atom at the end, so a head-only probe rejects a good file. Use the range trick for MIME sniffing and a full-file probe for the format assertion.

Retrying this error is the bug

Most pipelines that surface this error surface it five times, because someone wrapped the job in generic exponential backoff. An expired signed URL is still expired in fifteen minutes. A zero-byte file is still zero bytes. Classifying correctly saves more compute than any encoder tuning you'll do this quarter.

What you seeClassAction
`Invalid data found` before `Input #0`Bad inputReject now, no retry
`pipe:0: Invalid data found`Upstream bugFix the producer, no retry
`moov atom not found` then invalid dataTruncated fetchRe-fetch once, then reject
`Error while decoding stream #0:0`Bad packetsTry `-err_detect ignore_err`, then reject
Invalid data on a TS or RTSP sourceProbe too smallRetry once with `-probesize 50M`

Doing the guard yourself versus one API call

Running the guard yourself means keeping FFmpeg and ffprobe on every worker and every language runtime that touches media, in step with your production encoder version so the probe doesn't accept something the encoder later rejects.

Self-hosted ffprobe guardFFmpeg Micro
Binaries to install and patchffmpeg + ffprobe, every workernone
Fetch and Content-Length checksyour codehandled with the job
Failure outputraw stderr stringfailed job with a reason
Cost of a rejected filefull worker slotno render billed
Long uploads vs function timeoutsyour problemasync job

That last row bites n8n and Zapier builders hardest, since the validation download alone can outlast a workflow step. FFmpeg Micro takes the input URL, fetches and validates it as part of the job, and returns a failed job with a reason instead of a stderr line you have to grep. The docs cover the job semantics, and it works the same from code, from n8n, Make, and Zapier, or from an AI agent over MCP.

Common pitfalls

The most expensive mistake is trusting Content-Type. A bucket serves video/mp4 for an object whose bytes are an error page, because the header was set at upload time and never revalidated. Sniff the actual bytes.

Watch the race on shared volumes. A worker that triggers on file-create rather than on a completed write probes a file the writer still has open, and rejects valid uploads in rough proportion to their size. Probe on close, or on a rename into place.

Don't set -v error and throw the output away. Keep the last stderr line on the job record: "invalid data" versus moov atom not found is the difference between reject and re-fetch.

And don't make this guard your only validation. Files that pass the probe still break renders for other reasons, like odd pixel dimensions, covered in Fix FFmpeg's "height not divisible by 2" Error on User Uploads.

FAQ

Does "Invalid data found when processing input" mean my video file is corrupt?

The "Invalid data found when processing input" error usually means the bytes FFmpeg read were never a video file, not that a real video got damaged. On a service that processes other people's uploads, the most common cause is an HTTP error page or an S3 AccessDenied XML body written to disk with a .mp4 extension.

Why does FFmpeg say invalid data when the file plays fine in VLC?

If the file plays in VLC or QuickTime, the copy you're testing and the copy FFmpeg received are different bytes. Check the file size on the worker, run file --mime-type -b on the exact path FFmpeg was given, and compare the byte count to the source object's Content-Length.

How do I validate a video upload with ffprobe before processing?

Run ffprobe -v error -show_entries format=format_name,duration -show_streams -of json FILE and treat a non-zero exit code, an unexpected format_name, a duration of zero, or the absence of a video stream as a rejection. ffprobe reads the header only, so it finishes in well under a second on a local file of any size.

Writing that guard once per language, keeping ffprobe patched on every worker, and still owning the encoder underneath is a lot of surface area for a check that just says "this isn't a video." Probe-then-process runs as one API call on the free tier instead, so bad inputs come back as a labeled job failure and you never pay for a render that couldn't happen: sign up free.

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.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free