← All articles

[ Blog ]

July 9, 2026

8 min read

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.

macOSffmpegPermissionsCLITCCDevTools
ffmpeg "Operation Not Permitted" on macOS: 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:

  1. System Settings → Privacy & Security → Full Disk Access.
  2. Click + and add the host app you identified above (Cursor, VS Code, Terminal, iTerm2 — whatever owns the process chain).
  3. Toggle it on.
  4. 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?

SituationUse
You'll keep working from ~/Downloads/~/Desktop regularlyFull Disk Access (fix 1) — one-time setup
You just need this one file unblocked, right nowDrag into the project (fix 2) — no restart
You're driving commands from an AI coding agent's shellFix 2 first (no context-switch); fix 1 if it's a recurring pattern
CI or a headless script hits the same errorN/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.