n8n task runner rejections: poll a Sume job without losing it

n8n 2.41.6 keeps the task runner alive after an unhandled promise rejection. Make your Sume poll catch its own errors and stop only on a terminal status.

4 min readSume
All posts

Write a Sume status poll so no promise can reject unhandled: wrap each status read in try/catch, back off between reads, and stop only when the job is completed, failed or canceled. A thrown network error is not a job outcome.

What the n8n release says

The n8n releases page lists n8n@2.41.6, dated Oct 2, 2026, with the entry "Keep the task runner running on an unhandled promise rejection". That keeps the runner process up. It does not make the lost work come back, so the poll itself should still be defensive.

Poll rules from the Sume docs

The Sume jobs page gives the rules for a client-side poll.

  • Use exponential backoff, or honor next_poll_after_seconds when the status response carries it.
  • Stop on completed, failed or canceled.
  • Do not resubmit the original paid request because a local process timed out. Retrying the submit itself is fine with the same Idempotency-Key.
  • A client-side timeout does not cancel the job; it keeps running and billing.

A poll that handles its own errors

This Node 18+ function reads status until a terminal state. A failed fetch counts as one failed attempt rather than an escape. It reads the terminal and sume_status fields the status endpoint returns.

const API = "https://api.sume.com";
const key = process.env.SUME_API_KEY;
if (!key) throw new Error("SUME_API_KEY is not set");
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

export async function pollJob(jobId, deadlineMs = 20 * 60 * 1000) {
  const stop = Date.now() + deadlineMs;
  let delay = 3000;
  while (Date.now() < stop) {
    try {
      const res = await fetch(`${API}/v1/jobs/${jobId}/status`, {
        headers: { Authorization: `Bearer ${key}` },
      });
      if (res.ok) {
        const s = await res.json();
        if (s.terminal) return s;
        delay = (s.next_poll_after_seconds ?? 0) * 1000 || delay;
      }
    } catch (err) {
      console.error("status read failed, will retry", err.message);
    }
    await sleep(delay);
    delay = Math.min(delay * 2, 30000);
  }
  return { terminal: false, job_id: jobId, note: "deadline reached; job may still be running" };
}

Keep the job id outside the runner

Save the job id from the submit response somewhere durable before polling, such as a workflow data store. If the process dies mid-poll, a new run can resume from GET /v1/jobs/{id}/status. When the status is completed, call GET /v1/jobs/{id}/result once; before that it answers 409 job_not_completed.

For long video jobs, a webhook is cheaper than a long poll. Webhooks carry only the terminal events and are retried up to 10 attempts, so keep polling available for deliveries that never arrive.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume