Stirling-Tools/Stirling-PDF · warning · Error

policy requires interactive input and cannot run…

Error message

policy requires interactive input and cannot run automatically

What it means

Thrown during export-time policy enforcement when the backend policy run returns a status of WAITING_FOR_INPUT. The export path polls the policy runner via getPolicyRun() inside runToCompletion(); an interactive policy cannot be auto-resolved because there is no UI surface mid-export to capture user input, so the run is treated as a hard terminal state. The module header states export is never hard-blocked — the caller catches this and falls back to the original file with a warning toast.

Solutions

  1. Change the policy's runOn from 'export' to 'manual' so interactive steps never block the export path
  2. Configure the policy's backend to auto-resolve interactive decisions (auto-approve / auto-redact) so it reaches COMPLETED without WAITING_FOR_INPUT
  3. Split the policy: run the interactive portion on-demand before export, and only attach the deterministic portion to export enforcement
  4. Ensure the export caller catches this specific error and falls back to exporting the original file with a warning toast (the module is designed for this)

Example fix

// before
const result = await runToCompletion(backendId, file);
return result.file;

// after
try {
  const result = await runToCompletion(backendId, file);
  return result.file;
} catch (e) {
  if (e instanceof Error && e.message.includes('interactive input')) {
    alert(i18n.t('policies.enforcement.interactiveSkipped'), 'warning');
    return file; // fall back to original
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check if the policy has interactive steps before export
const policies = activeExportPolicies();
const hasInteractive = policies.some((p) => policyCatalogHasInteractiveSteps(p.categoryId));
if (hasInteractive) {
  // Warn user that interactive policies will fall back to original file
  showInteractivePolicyWarning();
}

Try / catch

try {
  const result = await runToCompletion(backendId, file);
  enforcedFile = result.file;
} catch (e) {
  if (e instanceof Error && e.message.includes('interactive input')) {
    enforcedFile = file; // fall back to original per module contract
    showToast('policies.enforcement.interactiveSkipped', 'warning');
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: A policy configured with runOn:'export' contains an interactive step (e.g. redaction approval, classification label selection, review checkpoint). The runStoredPolicy call succeeds and starts a run, but getPolicyRun polls return status 'WAITING_FOR_INPUT' before reaching COMPLETED.

Common situations: Policy authored with a human-review gate that is also flagged for automatic export enforcement; backend classification engine flags content it cannot auto-decide on; policy configuration drift where an interactive policy is accidentally set to runOn export.

Related errors


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

Appendix: source

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

      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", {
    first: names[0],
    second: names[1],
    more: names.length - 2,

View on GitHub (pinned to 9ef20dcab8)