How It Works Features Pricing Blog Error Guides
Log In Start Free Trial
Node.js · JavaScript

Fix Error: spawn /usr/bin/ffmpeg ENOENT in Node.js

This error means Node.js tried to spawn a child process for a binary that does not exist at the specified path. The executable is either not installed, not in the system PATH, or the path is incorrect. Fix it by installing the required binary and verifying its path with which or where.

Reading the Stack Trace

Error: spawn /usr/bin/ffmpeg ENOENT at ChildProcess._handle.onexit (node:internal/child_process:284:19) at onErrorNT (node:internal/child_process:477:16) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) at convertVideo (src/services/mediaService.js:23:18) at MediaController.transcode (src/controllers/mediaController.js:14:22) at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5) at next (node_modules/express/lib/router/route.js:144:13) at Route.dispatch (node_modules/express/lib/router/route.js:114:3) at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5) at /node_modules/express/lib/router/index.js:284:15

Here's what each line means:

Common Causes

1. External binary not installed

The required executable (ffmpeg, imagemagick, etc.) is not installed on the system or in the Docker image.

const { spawn } = require('child_process');
const proc = spawn('/usr/bin/ffmpeg', ['-i', input, output]);
// ffmpeg is not installed

2. Binary not in PATH

The binary is installed but not in the system PATH, or the code uses an absolute path that differs between environments.

const proc = spawn('/usr/local/bin/ffmpeg', args);
// On this system ffmpeg is at /usr/bin/ffmpeg

3. Incorrect shell option for commands with arguments

Passing a full command string with arguments without shell: true causes spawn to look for a binary named with the arguments included.

spawn('ffmpeg -i input.mp4 output.webm'); // Should use array args

The Fix

Use which to dynamically find the binary path instead of hardcoding it. Handle the 'error' event on the spawned process to catch ENOENT and provide a helpful message about installing the missing dependency.

Before (broken)
const { spawn } = require('child_process');

function convertVideo(input, output) {
  const proc = spawn('/usr/bin/ffmpeg', ['-i', input, output]);
  return new Promise((resolve, reject) => {
    proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`ffmpeg exited with ${code}`)));
  });
}
After (fixed)
const { spawn, execSync } = require('child_process');

function findBinary(name) {
  try {
    return execSync(`which ${name}`, { encoding: 'utf8' }).trim();
  } catch {
    throw new Error(`Required binary '${name}' not found. Install it with: apt-get install ${name}`);
  }
}

function convertVideo(input, output) {
  const ffmpegPath = findBinary('ffmpeg');
  const proc = spawn(ffmpegPath, ['-i', input, output]);

  return new Promise((resolve, reject) => {
    proc.on('error', (err) => reject(new Error(`Failed to start ffmpeg: ${err.message}`)));
    proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`ffmpeg exited with code ${code}`)));
  });
}

Testing the Fix

const { convertVideo, findBinary } = require('./mediaService');
const child_process = require('child_process');

jest.mock('child_process');

describe('convertVideo', () => {
  it('throws when ffmpeg is not installed', () => {
    child_process.execSync.mockImplementation(() => { throw new Error(); });
    expect(() => findBinary('ffmpeg')).toThrow("Required binary 'ffmpeg' not found");
  });

  it('resolves when ffmpeg exits with code 0', async () => {
    child_process.execSync.mockReturnValue('/usr/bin/ffmpeg\n');
    const mockProc = { on: jest.fn() };
    child_process.spawn.mockReturnValue(mockProc);
    const promise = convertVideo('in.mp4', 'out.webm');
    const closeHandler = mockProc.on.mock.calls.find(c => c[0] === 'close')[1];
    closeHandler(0);
    await expect(promise).resolves.toBeUndefined();
  });
});

Run your tests:

npm test

Pushing Through CI/CD

git checkout -b fix/nodejs-child-process-enoent,git add src/services/mediaService.js src/services/__tests__/mediaService.test.js,git commit -m "fix: dynamically resolve binary path and handle spawn errors",git push origin fix/nodejs-child-process-enoent

Your CI config should look something like this:

name: CI
on:
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: sudo apt-get update && sudo apt-get install -y ffmpeg
      - run: npm ci
      - run: npm test -- --coverage

The Full Manual Process: 18 Steps

Here's every step you just went through to fix this one bug:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. Monitor production to confirm the error is resolved

Total time: 30-60 minutes. For one bug.

Or Let bugstack Fix It in Under 2 minutes

Every step above? bugstack does it automatically.

Step 1: Install the SDK

npm install bugstack-sdk

Step 2: Initialize

const { initBugStack } = require('bugstack-sdk')

initBugStack({ apiKey: process.env.BUGSTACK_API_KEY })

Step 3: There is no step 3.

bugstack handles everything from here:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. Deploys to production (or opens a PR for review)

Time from error to fix deployed: Under 2 minutes.

Human involvement: zero.

Try bugstack Free →

No credit card. 5-minute setup. Cancel anytime.

Deploying the Fix (Manual Path)

  1. Install the required binary on the target system or add it to the Dockerfile.
  2. Update code to dynamically locate the binary and handle errors.
  3. Run tests locally to verify the fix.
  4. Open a pull request and wait for CI.
  5. Merge and verify the binary is available in the deployment environment.

Frequently Asked Questions

BugStack runs the fix through your existing test suite, generates additional edge-case tests, and validates that no other modules are affected before marking it safe to deploy.

BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.

Add 'RUN apt-get update && apt-get install -y ffmpeg' to your Dockerfile before the CMD instruction. For Alpine-based images use 'apk add --no-cache ffmpeg'.

Use spawn for long-running processes with large output (like video transcoding) since it streams data. Use exec for short commands where you need the full output as a string.