← All articles
ffmpeg "Operation Not Permitted" on macOS: The Real Fix
Get "Operation not permitted" running ffmpeg on a file in Downloads on macOS? It's TCC blocking your terminal app, not a broken install — here's the real fix.

I dropped a video into ~/Downloads, ran a one-line ffmpeg command against it from my terminal,
and got a wall of Operation not permitted. Not a codec error, not a missing dependency — a flat
permission denial, on a file I could see sitting right there in Finder. If you've hit "operation
not permitted" running ffmpeg on a file in Downloads on macOS, the cause isn't ffmpeg, and it
isn't your shell. It's macOS itself quietly blocking the app your terminal runs inside. Here's how
I traced it and the two ways to actually fix it.
The symptom: ls works, ffmpeg doesn't
The first thing that makes this bug confusing is that it's inconsistent — some commands work fine, others fail outright on the exact same file:
$ ls -la ~/Downloads/clip.mov
-rw-r--r--@ 1 hon staff 48213991 Jul 9 10:02 clip.mov
$ ffmpeg -i ~/Downloads/clip.mov -c:v libx264 -crf 20 out.mp4
[in#0 @ 0x14b006a80] Error opening input: Operation not permitted
Error opening input file ~/Downloads/clip.mov.
Error opening input files: Operation not permitted
ls reads directory metadata — that's cached and mostly unguarded. ffmpeg needs to actually
open the file for reading, and that's the operation macOS intercepts. The same thing happens
with plain cat:
$ cat ~/Downloads/clip.mov > /dev/null
cat: /Users/hon/Downloads/clip.mov: Operation not permitted
cp behaves the same way. At this point it's tempting to blame ffmpeg's build, a corrupted
Homebrew install, or some sandbox flag in whatever AI coding agent or terminal you're driving the
command from. I burned twenty minutes going down that path before I checked the one thing that
actually explains it: macOS TCC.
It's TCC, not a tool sandbox
TCC (Transparency, Consent, and Control) is the macOS privacy layer that governs which apps
can touch protected user data — camera, microphone, contacts, and, since Catalina, a set of
protected folders: ~/Downloads, ~/Desktop, ~/Documents, plus anything under iCloud Drive.
This protection isn't per-process or per-CLI-tool; it's granted to the application bundle
that spawned the process. Apple's own docs on the mechanism are here: TCC and privacy
protections for apps,
and the developer-facing reference lives under
App Sandbox and privacy.
The key thing I got wrong at first: I assumed this was about the CLI tool itself — some sandbox
entitlement on ffmpeg, or a restriction inside an AI coding agent's shell. It's neither. When any
process reads a file under a TCC-protected folder, macOS walks up the parent process chain to
find the actual GUI application responsible — your terminal, your editor, whatever spawned the
shell — and checks that app's Full Disk Access grant. If it's a VS Code fork like Cursor, or any
terminal app that hasn't been explicitly granted access, the read is denied, no matter how
innocent the tool asking for it is. Disabling a CLI sandbox flag, reinstalling ffmpeg, or switching
shells changes nothing here, because none of those are the thing macOS is checking.
Finding the app TCC is actually blocking
Before granting anything, confirm which app is on the hook. Two quick checks from inside the shell that's failing:
$ echo $__CFBundleIdentifier
com.todesktop.230313mzl4w4u92 # Cursor, in my case — a VS Code fork
If that variable is empty or unhelpful, walk the parent process chain instead — this works from any shell, agent-driven or not:
$ ps -o pid,ppid,comm -p $$
PID PPID COMM
12345 6789 /bin/zsh
$ ps -o pid,ppid,comm -p 6789
PID PPID COMM
6789 1 /Applications/Cursor.app/Contents/MacOS/Cursor
Whatever .app shows up at the top of that chain is the one that needs Full Disk Access — not the
shell, not ffmpeg, not the AI coding agent driving it. An AI coding agent that can't read a file
in Downloads is almost always this exact chain: the agent runs a shell, the shell inherits the
host app's TCC identity, and the host app has never been granted access.
Fix 1: grant Full Disk Access, then fully quit
This is the durable fix if you'll keep working out of ~/Downloads, ~/Desktop, or
~/Documents:
- System Settings → Privacy & Security → Full Disk Access.
- Click + and add the host app you identified above (Cursor, VS Code, Terminal, iTerm2 — whatever owns the process chain).
- Toggle it on.
- Fully quit the app —
⌘Q, not just closing the window — and reopen it.
Step 4 is the part people skip and then report the fix "doesn't work." TCC grants are evaluated
when the app's process starts; a running process keeps its old (denied) state until it's
relaunched. If you re-run the same ffmpeg command in the same still-open terminal window right
after flipping the toggle, you'll get the exact same Operation not permitted and conclude the
setting did nothing. Quit, relaunch, retry.
This is also why disabling any sandbox setting inside the tool itself never helps: the check happens one layer up, at the OS level, against the app bundle — not against ffmpeg's own permissions.
Fix 2: move the file into the project instead
If you don't want to touch system permissions — or you're driving this from an AI coding agent where you'd rather not grant a host app blanket Full Disk Access — there's a faster workaround that needs no restart:
# Finder-driven copy, not a terminal cp:
# drag clip.mov from ~/Downloads into your project folder, e.g. ./assets/
$ ffmpeg -i ./assets/clip.mov -c:v libx264 -crf 20 out.mp4
# works immediately
The trick is how the file moves. Dragging it in Finder uses Finder's own TCC grant (Finder
always has access to your user folders), so the copy that lands inside your project directory is
no longer under a protected path — your repo directory was never one of the folders TCC guards in
the first place. A cp run from the same blocked terminal won't help, because that cp inherits
the same denied identity as the ffmpeg call. It has to be a Finder-side move or copy.
This is the fix I actually used the day this bit me: I was mid-flow
encoding a scroll-scrubbed hero video and didn't want
to stop, grant permissions, and relaunch an editor mid-task. Dragging the source clip into the
project's assets/ folder unblocked the encode in about five seconds.
Which fix should you use?
| Situation | Use |
|---|---|
You'll keep working from ~/Downloads/~/Desktop regularly | Full Disk Access (fix 1) — one-time setup |
| You just need this one file unblocked, right now | Drag into the project (fix 2) — no restart |
| You're driving commands from an AI coding agent's shell | Fix 2 first (no context-switch); fix 1 if it's a recurring pattern |
| CI or a headless script hits the same error | N/A — TCC is a GUI-app-driven, per-user setting; it doesn't apply to non-interactive CI runners the same way, so investigate your runner's file ownership instead |
FAQ
Why does ls work but cat or ffmpeg fails with permission denied on Mac?
ls reads cached directory metadata, which macOS doesn't gate behind TCC. Opening the file's
actual contents — what cat, cp, and ffmpeg all need to do — triggers the TCC check against
the parent app. Metadata and content access are checked differently; that's the whole source of
the confusion.
I have Cursor/a terminal app open — why is Full Disk Access still "permission denied"?
Because TCC grants apply at process start, not live. If the app was already running when you
granted access, it's still operating under its old, denied permission state. Fully quit with
⌘Q and reopen — a window close or a new tab isn't enough, the whole process has to restart.
Can an AI coding agent read a file in Downloads if I disable its sandbox?
No. This is the mistake I made first. The agent's shell tools aren't what TCC is checking — it's checking the GUI application that spawned the shell (the editor or terminal itself). Disabling a tool-level sandbox setting has no effect on an OS-level, app-identity-based permission system.
Does this affect only Downloads, or other folders too?
TCC's "Files and Folders" protection covers ~/Downloads, ~/Desktop, ~/Documents, and iCloud
Drive locations by default, plus anything you've explicitly protected. A project directory you
created yourself under, say, ~/dev or ~/Documents/works is only protected if it lives inside
one of those guarded roots — move your working files outside them (or into an already-trusted
app's granted folder) and the check doesn't apply.
Is there a command-line way to check or reset TCC permissions?
Apple doesn't ship a fully supported CLI for querying per-app TCC grants — System Settings is the sanctioned path. If a grant looks stuck or wrong, removing the app from the Full Disk Access list and re-adding it (followed by a full quit/reopen) resets its state cleanly.
If you're piping media through ffmpeg as part of a bigger pipeline — I ran into this exact wall
while encoding a scroll-scrubbed hero video for a
scroll-driven site — it's worth knowing this gotcha exists before it eats twenty minutes of your
afternoon. And if you'd rather hand off the whole animation/video pipeline to someone who's already
debugged this stuff, see how I work with teams.