{"record":{"id":"7c3fe7d1e235a46d","repo":"Stirling-Tools/Stirling-PDF","slug":"view-error-policy-run-view-status-tolowercase","errorCode":null,"errorMessage":"view.error || policy run ${view.status.toLowerCase()}","messagePattern":"view\\.error \\|\\| policy run (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/services/policyExport.ts","lineNumber":110,"sourceCode":"    await delay(POLL_MS);\n    let view;\n    try {\n      view = await getPolicyRun(runId);\n    } catch {\n      continue; // transient — keep polling within the cap.\n    }\n    if (view.status === \"COMPLETED\") {\n      const out = view.outputs?.[0];\n      if (!out) throw new Error(\"policy produced no output\");\n      const blob = await downloadPolicyOutput(out.fileId, target);\n      // Keep the export's filename; only the bytes are the enforced result.\n      const enforced = new File([blob], file.name, {\n        type: blob.type || file.type || \"application/pdf\",\n      });\n      return { file: enforced, runId, target, outputs: view.outputs ?? [] };\n    }\n    if (view.status === \"FAILED\" || view.status === \"CANCELLED\") {\n      throw new Error(view.error || `policy run ${view.status.toLowerCase()}`);\n    }\n    if (view.status === \"WAITING_FOR_INPUT\") {\n      throw new Error(\n        \"policy requires interactive input and cannot run automatically\",\n      );\n    }\n  }\n  throw new Error(\"policy run timed out\");\n}\n\nfunction enforcedFilesSummary(names: string[]): string {\n  if (names.length === 1) return names[0];\n  if (names.length === 2)\n    return i18n.t(\"policies.enforcement.summaryTwo\", {\n      first: names[0],\n      second: names[1],\n    });\n  return i18n.t(\"policies.enforcement.summaryMore\", {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/services/policyExport.ts#L92-L128","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read view.error (surfaced in the thrown message) for the backend's failure reason — it usually names the failing step.","Open the policy run detail in the UI (runId) for the full step-level error log.","If the input file is corrupt/password-protected, validate it before enforcing or exclude such files from the policy scope.","Confirm the automation's referenced tools/scripts still exist and are healthy on the backend.","The export wrapper catches this and falls back to exporting the original file with a warning toast; log the runId for post-hoc review."],"exampleFix":"// before\nif (view.status === \"FAILED\" || view.status === \"CANCELLED\") {\n  throw new Error(view.error || `policy run ${view.status.toLowerCase()}`);\n}\n\n// after — preserve the error but tag it so callers can distinguish\nif (view.status === \"FAILED\" || view.status === \"CANCELLED\") {\n  const msg = view.error || `policy run ${view.status.toLowerCase()}`;\n  const e = new Error(msg);\n  (e as any).policyRunId = runId;\n  (e as any).policyStatus = view.status;\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// pre-validate input file health to avoid backend FAILED on corrupt PDFs\nif (!isPdf(file) || await isEncryptedPdf(file)) {\n  skipEnforcement(file, 'unsupported input');\n}","typeGuard":"export function isPolicyRunFailure(e: unknown): e is Error {\n  return e instanceof Error && /policy run (failed|cancelled)|policy produced|timed out|interactive input/i.test(e.message);\n}","tryCatchPattern":"// export wrapper treats this as non-fatal: export the original + warn\ntry {\n  enforced = await runToCompletion(backendId, file);\n} catch (e) {\n  if (isPolicyRunFailure(e)) {\n    warnExportWithOriginal(file, e);\n    enforced = { file, runId: undefined, target, outputs: [] };\n  } else throw e;\n}","preventionTips":["Validate input PDFs (not corrupt, not encrypted) before enforcing a policy on them.","Keep the automation's referenced tools/scripts present and healthy on the backend.","Log the runId from the thrown error so backend step-level failures can be investigated.","Treat FAILED/CANCELLED as expected during export and fall back to the original file with a user-visible warning."],"tags":["policies","export","enforcement","backend-failure","proprietary","typescript"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}