n8nvideo-processingapi

How to Fix n8n Timeout Errors When Processing Video

··10 min read
How to Fix n8n Timeout Errors When Processing Video

Your n8n workflow builds fine, runs on a short test clip, then dies the moment you feed it a real video. The execution turns red with a timeout, the render never finishes, and you have no idea whether the job actually ran. This is the classic wall n8n builders hit the first time they put video processing inside a workflow.

Quick answer: An n8n video processing timeout happens because n8n Cloud caps a single execution at roughly 2 to 5 minutes, and a real FFmpeg transcode or composition takes longer than that. The fix is to stop running the video work inside the execution: submit the job to an async video API that returns a job ID right away, then poll for the result from a Wait loop or a scheduled sweep.

Why n8n times out on video jobs

The timeout isn't a bug in your workflow. It's n8n doing exactly what it's designed to do, killing any execution that runs too long so one stuck job doesn't hold a worker forever.

Two nodes create this problem, and they fail for different reasons.

The Execute Command node is the one people reach for to shell out to a local FFmpeg binary. It isn't available on n8n Cloud at all, so ffmpeg -i in.mp4 ... simply isn't an option there. Even on self-hosted n8n, the command blocks the execution thread until FFmpeg finishes, and a two-minute 1080p transcode will blow past the execution timeout while pinning your worker's CPU.

The HTTP Request node is the other trap. When you call a slow synchronous endpoint that does the encoding before it responds, n8n holds the connection open and waits. The node has its own request timeout, but the real killer is the workflow-level cap. n8n Cloud limits a single workflow execution to roughly 2 to 5 minutes depending on your plan tier, and self-hosted instances enforce the same thing through the EXECUTIONS_TIMEOUT environment variable. Any video job longer than that window gets terminated mid-render, and n8n marks the execution failed even though the work never had a chance to complete.

That's the mechanism. You're trying to run a long job inside a system built to run short ones.

The fix: offload the render, don't hold the execution open

The fix for an n8n video timeout isn't a longer timeout or a beefier worker. It's not running the render inside the n8n execution at all.

Instead of one node that waits for FFmpeg to finish, you split the work into two fast interactions:

  1. Submit the job to a video API. The API accepts the request, queues the render, and returns a job ID in well under a second. Your n8n execution finishes immediately, nowhere near the timeout.
  2. Collect the result later, by polling the job status on a short interval, in a Wait loop or from a second scheduled workflow.

FFmpeg Micro is built around exactly these job semantics: submit a job to api.ffmpeg-micro.com, then poll its status, then download the output. One API call replaces the media microservice you'd otherwise babysit. The heavy FFmpeg work runs on managed infrastructure, so there's no binary to install, no server to scale, and nothing for n8n to wait on.

Here are both patterns as copy-paste recipes. Check /docs for the exact request body fields for your operation.

Recipe 1: the polling pattern

Use the Wait loop when you want everything in a single linear workflow. You submit the job, wait a few seconds, check status, and loop until it's done.

The node sequence: HTTP Request (create job) leads to Wait, then HTTP Request (get status), then IF (done? loop back or continue).

The create-job request sends a small JSON body. This is the payload the HTTP Request node posts to /jobs:

{
  "operation": "transcode",
  "input": "https://example.com/input.mp4"
}

In n8n, that maps to an HTTP Request node with sendBody enabled and the body set to JSON. Here is the full node export, with the input URL pulled from an incoming field:

{
  "name": "Create job",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "method": "POST",
    "url": "https://api.ffmpeg-micro.com/jobs",
    "authentication": "genericCredentialType",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ operation: 'transcode', input: $json.videoUrl }) }}"
  }
}

Building the body with JSON.stringify inside the expression avoids hand-escaping quotes, which is where these nodes usually break. The rest of the loop is three more nodes:

{
  "nodes": [
    {
      "name": "Wait 5s",
      "type": "n8n-nodes-base.wait",
      "parameters": { "amount": 5, "unit": "seconds" }
    },
    {
      "name": "Get status",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "method": "GET",
        "url": "=https://api.ffmpeg-micro.com/jobs/{{ $('Create job').item.json.id }}"
      }
    },
    {
      "name": "Done?",
      "type": "n8n-nodes-base.if",
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.status }}",
              "operation": "notEqual",
              "value2": "completed"
            }
          ]
        }
      }
    }
  ]
}

Wire the true branch of the IF node (status not completed) back into Wait 5s so it loops, and the false branch forward to your download or next step. Because each cycle is a sub-second HTTP call plus a short wait, the whole loop stays inside the execution window even for a five-minute render. Keep the wait interval sensible: 5 to 10 seconds is plenty, and hammering the status endpoint every 500 ms just burns executions.

Recipe 2: the scheduled-sweep pattern

Use the sweep pattern when you don't want one execution sitting in a loop per job. Workflow A submits the job, records the returned id somewhere durable (a sheet, a data table, a database row), and ends. A second workflow on a Schedule trigger sweeps every minute, polls each pending id, and handles the ones that finished. This is the cleaner option for long jobs and high volume.

Workflow A submits the job and stores the id it gets back:

{
  "name": "Submit job",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "method": "POST",
    "url": "https://api.ffmpeg-micro.com/jobs",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ operation: 'compose', input: $json.videoUrl }) }}"
  }
}

Workflow B runs on the schedule, checks each pending job, and handles the finished output:

{
  "nodes": [
    {
      "name": "Every minute",
      "type": "n8n-nodes-base.scheduleTrigger",
      "parameters": { "rule": { "interval": [{ "field": "minutes" }] } }
    },
    {
      "name": "Check job",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "method": "GET",
        "url": "={{ 'https://api.ffmpeg-micro.com/jobs/' + $json.jobId }}"
      }
    },
    {
      "name": "Download output",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "method": "GET",
        "url": "={{ $json.outputUrl }}"
      }
    }
  ]
}

No held-open execution: each sweep run finishes in seconds however long the renders take, and n8n only works when there's something to collect. This is the same offload approach we use for assembling faceless-channel videos from assets and for concatenating clips programmatically, where a single workflow can kick off dozens of renders without ever touching the timeout.

Loop or sweep: which to use

Both patterns solve the timeout. Pick based on volume and how much infrastructure you want to expose.

FactorWait loopScheduled sweep
SetupSingle workflowTwo workflows, plus somewhere to keep pending ids
Best forLow volume, quick jobsLong jobs, high volume, batches
n8n execution costOne execution that loopsTwo short executions
Failure visibilityEasy to see in one runSplit across two runs
Requires public endpointNoYes

If you're testing or running a handful of jobs a day, polling is less to set up. Once you're rendering dozens of videos or your jobs regularly run past a minute, the sweep pattern keeps your execution list clean and your workers free.

Common pitfalls

These are the mistakes that keep the timeout coming back even after you've split the work.

  • Still using a synchronous endpoint. If your "submit" call waits for the render before responding, you haven't offloaded anything. The create-job request must return a job ID immediately, not the finished file.
  • Hand-escaping the JSON body. Typing \"operation\": \"transcode\" into the jsonBody field by hand is where most of these nodes break. Build the body with JSON.stringify({ ... }) in an expression and let n8n produce valid JSON for you.
  • Polling too aggressively. A Wait of 500 ms turns one render into hundreds of status calls and can itself approach the execution limit. Use 5 to 10 second intervals.
  • No max-attempts guard. An infinite IF loop with no counter will run until the execution times out if a job gets stuck. Add a counter and bail after, say, 60 checks.
  • A sweep with no ledger. If nothing durable records which ids are pending, the sweep has nothing to check and finished jobs vanish. Write the id at submit time to a sheet, table or row, never to workflow memory.
  • Passing giant files through n8n. Don't pull a 500 MB output into an n8n binary field just to move it. Pass the output URL downstream and let the destination fetch it.

When not to offload (and when to choose something else)

Offloading to a video API is the right call when your job is long, bursty, or CPU-heavy. It's overkill for a two-second thumbnail grab on a tiny clip, where a self-hosted Execute Command node might finish inside the window just fine.

It's also not the answer if you need a human-facing video editor with a timeline UI, or if your workflow is really about live streaming rather than file-based rendering. FFmpeg Micro is an API for batch media processing, not an editing suite or a streaming server. For heavy in-house pipelines you already run on AWS, tools like AWS MediaConvert or a self-managed FFmpeg fleet may fit your billing better. The trade-off you accept by not offloading is the one you started with: n8n holding a worker open and a timeout waiting to happen. The same offload logic applies anywhere serverless kills long jobs, which is why Supabase Edge Functions can't run FFmpeg either.

FAQ

Why does my n8n workflow time out when processing video?

Because n8n Cloud caps a single execution at roughly 2 to 5 minutes and a real FFmpeg transcode or composition runs longer than that. The HTTP Request node holds the connection open while the encode runs, and the workflow-level timeout kills the execution before the render finishes.

Can I run FFmpeg directly in n8n?

Only on self-hosted n8n, using the Execute Command node to call a local FFmpeg binary. It's not available on n8n Cloud, and even self-hosted it blocks the execution thread and counts against your EXECUTIONS_TIMEOUT, so long jobs still fail. Calling a hosted FFmpeg API avoids both problems.

How do I handle a long-running video job in n8n?

Submit the job to a video API that returns a job ID immediately, then poll the status endpoint, on a 5 to 10 second Wait loop or from a scheduled sweep workflow. Either way, no single execution stays open long enough to time out.

How do I send a JSON body from the n8n HTTP Request node without breaking it?

Enable sendBody, set the body type to JSON, and build the payload with an expression like ={{ JSON.stringify({ operation: 'transcode', input: $json.videoUrl }) }}. This produces valid, correctly escaped JSON instead of the malformed body you get from hand-typing escaped quotes.

Does the loop or the sweep use more n8n executions?

The Wait loop uses one execution that stays open and loops with Wait and IF nodes, while the sweep uses many short executions, one per minute plus one per submission. For high volume, the sweep keeps each execution short and your execution log readable.

Spin up the loop recipe on a test video first, then switch to the sweep once you're rendering at volume. You can grab an API key and run both against the free tier in a few minutes: sign up here.

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