Stirling-Tools/Stirling-PDF · error · Error

policy run timed out

Error message

policy run timed out

What it means

Thrown when the policy enforcement poll loop exhausts all MAX_POLLS (75) iterations at POLL_MS (2000ms) — roughly 150 seconds / 2.5 minutes — without the backend run reaching a terminal status (COMPLETED, FAILED, CANCELLED, or WAITING_FOR_INPUT). Each getPolicyRun() call that throws is silently swallowed with `continue`, which still consumes a poll slot, so transient network errors accelerate budget exhaustion.

Source

Thrown at frontend/editor/src/proprietary/services/policyExport.ts:118

      const out = view.outputs?.[0];
      if (!out) throw new Error("policy produced no output");
      const blob = await downloadPolicyOutput(out.fileId, target);
      // Keep the export's filename; only the bytes are the enforced result.
      const enforced = new File([blob], file.name, {
        type: blob.type || file.type || "application/pdf",
      });
      return { file: enforced, runId, target, outputs: view.outputs ?? [] };
    }
    if (view.status === "FAILED" || view.status === "CANCELLED") {
      throw new Error(view.error || `policy run ${view.status.toLowerCase()}`);
    }
    if (view.status === "WAITING_FOR_INPUT") {
      throw new Error(
        "policy requires interactive input and cannot run automatically",
      );
    }
  }
  throw new Error("policy run timed out");
}

function enforcedFilesSummary(names: string[]): string {
  if (names.length === 1) return names[0];
  if (names.length === 2)
    return i18n.t("policies.enforcement.summaryTwo", {
      first: names[0],
      second: names[1],
    });
  return i18n.t("policies.enforcement.summaryMore", {
    first: names[0],
    second: names[1],
    more: names.length - 2,
  });
}

/**
 * Enforce every active export-policy on each PDF just before export, returning

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Check the backend policy service logs for stuck or long-running policy executions
  2. If large-file processing legitimately exceeds 2.5 min, increase MAX_POLLS or POLL_MS constants
  3. Decouple the transient-error budget from the time budget so getPolicyRun failures don't consume poll slots (e.g. retry the fetch within a cycle before advancing i)
  4. Reduce the input PDF size or split the operation so individual policy runs complete faster

Example fix

// before
for (let i = 0; i < MAX_POLLS; i++) {
  await delay(POLL_MS);
  try {
    view = await getPolicyRun(runId);
  } catch {
    continue; // burns a poll slot
  }
  ...
}

// after
const deadline = Date.now() + MAX_POLLS * POLL_MS;
while (Date.now() < deadline) {
  await delay(POLL_MS);
  let view;
  try {
    view = await getPolicyRun(runId);
  } catch {
    continue; // doesn't consume a fixed slot
  }
  ...terminal checks...
}
Defensive patterns

Strategy: retry

Validate before calling

// Estimate processing time before export
const estimatedMs = estimatePolicyProcessingTime(file.size, policyComplexity);
if (estimatedMs > MAX_POLLS * POLL_MS) {
  warnUserLargeFileMayTimeOut();
}

Try / catch

try {
  const result = await runToCompletion(backendId, file);
} catch (e) {
  if (e instanceof Error && e.message.includes('timed out')) {
    // Retry once — the backend run may have completed but polling expired
    const view = await getPolicyRun(runId).catch(() => null);
    if (view?.status === 'COMPLETED') {
      return downloadAndProcessOutput(view);
    }
  }
  throw e;
}

Prevention

When it happens

Trigger: The backend policy run stays in PENDING or RUNNING past the 75-poll window. This happens when the PDF is very large and processing exceeds ~2.5 minutes, when getPolicyRun repeatedly throws transient errors (each catch{continue} burns a cycle), or when the backend policy service is overloaded/stuck.

Common situations: Processing a very large PDF (100GB-class) through a compute-heavy policy; backend policy pod restarted mid-run leaving the run orphaned; network latency or intermittent 5xx from the policy API eating poll cycles; policy service under load in production.

Understand the failure class

Related errors


AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13). Data as JSON: /api/errors/9fd37d1f66cae9b5. Report an issue: GitHub.