{"record":{"id":"1f9835a91a43f7a7","repo":"Stirling-Tools/Stirling-PDF","slug":"policy-produced-no-output","errorCode":null,"errorMessage":"policy produced no output","messagePattern":"policy produced no output","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/services/policyExport.ts","lineNumber":101,"sourceCode":"/** Run one policy on a file and resolve the enforced bytes + run info (throws on\n *  failure). */\nasync function runToCompletion(\n  backendId: string,\n  file: File,\n): Promise<PolicyRunResult> {\n  const target = resolvePolicyRunTarget();\n  const runId = await runStoredPolicy(backendId, [file]);\n  for (let i = 0; i < MAX_POLLS; i++) {\n    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}","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/services/policyExport.ts#L83-L119","documentation":"Thrown by runToCompletion in policyExport when a policy run reaches COMPLETED status but view.outputs[0] is missing — i.e. the pipeline finished successfully yet produced no downloadable file. The export path needs at least one output file to build the enforced PDF, so a completion with zero outputs is treated as a hard failure (the policy didn't do its job) rather than silently exporting the original.","triggerScenarios":"The stored policy's automation has no file-producing operation; the backend marked the run COMPLETED but the output artifact was deleted/pruned before poll; an automation that only mutates metadata (no output file) was set as an export policy; a bug where the backend writes outputs to a different array index.","commonSituations":"User configures a policy whose operations don't emit a file (e.g. a metadata-only step) and sets runOn=export; backend output retention misconfigured so outputs are GC'd; a policy template was authored without a terminal output step.","solutions":["Inspect the policy automation to confirm it ends in a file-producing operation (e.g. save/convert) before the policy is set to runOn=export.","Check the backend policy-run detail for outputs — confirm whether they were ever written or pruned.","Increase output retention if artifacts are being GC'd before the poll completes.","Surface the error to the user (the export wrapper falls back to the original file + warning toast) and log the runId for diagnosis."],"exampleFix":"// before\nif (!out) throw new Error(\"policy produced no output\");\n\n// after — keep the throw, but the caller already falls back to the original file\nif (!out) {\n  logPolicyRunAnomaly(runId, 'completed-without-output');\n  throw new Error(\"policy produced no output\");\n}","handlingStrategy":"try-catch","validationCode":"// before setting runOn=export, confirm the automation ends in a file-producing operation\nconst endsWithOutput = automation.operations.some((op) => producesFile(op));\nif (!endsWithOutput) warn('This policy produces no file; it cannot enforce on export.');","typeGuard":"export function policyProducesFile(automation: { operations: Operation[] }): boolean {\n  return automation.operations.some((op) => op.type === 'save' || op.type === 'convert');\n}","tryCatchPattern":"// the export wrapper already falls back to the original file on this error:\ntry {\n  enforced = await runToCompletion(backendId, file);\n} catch (e) {\n  if (/no output|timed out|FAILED|CANCELLED|interactive input/i.test(e instanceof Error ? e.message : \"\")) {\n    warnExportWithOriginal(file, e);\n    enforced = { file, runId: undefined, target, outputs: [] };\n  } else throw e;\n}","preventionTips":["Validate that a policy automation ends in a file-producing operation before allowing runOn=export.","Monitor policy runs for 'completed-without-output' anomalies via the runId.","Keep output retention long enough to outlast the poll window (≈2.5 min)."],"tags":["policies","export","enforcement","data-integrity","proprietary","typescript"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}