Stirling-Tools/Stirling-PDF · error · Error

view.error || policy run ${view.status.toLowerCase()}

Error message

view.error || policy run ${view.status.toLowerCase()}

What it means

Thrown by runToCompletion when getPolicyRun returns a status of FAILED or CANCELLED. The message is view.error if the backend supplied one, otherwise a synthesized 'policy run failed'/'policy run cancelled' from the lowercased status. This is the backend reporting the policy pipeline itself failed or was cancelled mid-run (as opposed to a transport error, which is swallowed and retried in the getPolicyRun try/catch).

Source

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

    await delay(POLL_MS);
    let view;
    try {
      view = await getPolicyRun(runId);
    } catch {
      continue; // transient — keep polling within the cap.
    }
    if (view.status === "COMPLETED") {
      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", {

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Read view.error (surfaced in the thrown message) for the backend's failure reason — it usually names the failing step.
  2. Open the policy run detail in the UI (runId) for the full step-level error log.
  3. If the input file is corrupt/password-protected, validate it before enforcing or exclude such files from the policy scope.
  4. Confirm the automation's referenced tools/scripts still exist and are healthy on the backend.
  5. The export wrapper catches this and falls back to exporting the original file with a warning toast; log the runId for post-hoc review.

Example fix

// before
if (view.status === "FAILED" || view.status === "CANCELLED") {
  throw new Error(view.error || `policy run ${view.status.toLowerCase()}`);
}

// after — preserve the error but tag it so callers can distinguish
if (view.status === "FAILED" || view.status === "CANCELLED") {
  const msg = view.error || `policy run ${view.status.toLowerCase()}`;
  const e = new Error(msg);
  (e as any).policyRunId = runId;
  (e as any).policyStatus = view.status;
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-validate input file health to avoid backend FAILED on corrupt PDFs
if (!isPdf(file) || await isEncryptedPdf(file)) {
  skipEnforcement(file, 'unsupported input');
}

Type guard

export function isPolicyRunFailure(e: unknown): e is Error {
  return e instanceof Error && /policy run (failed|cancelled)|policy produced|timed out|interactive input/i.test(e.message);
}

Try / catch

// export wrapper treats this as non-fatal: export the original + warn
try {
  enforced = await runToCompletion(backendId, file);
} catch (e) {
  if (isPolicyRunFailure(e)) {
    warnExportWithOriginal(file, e);
    enforced = { file, runId: undefined, target, outputs: [] };
  } else throw e;
}

Prevention

When it happens

Trigger: A pipeline operation threw on the backend (e.g. a conversion failed on a corrupt PDF); the policy automation references a missing tool/script; an admin or system event cancelled the run; the run exceeded backend resource limits and was marked FAILED; the input file triggered a guard the policy enforces.

Common situations: Policy automation points at a tool that was removed in a release; the source PDF is corrupt or password-protected and a step can't process it; backend PDF engine (LibreOffice) crashed on the input; user cancelled from the runs UI; backend OOM killed the run.

Related errors


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