Stirling-Tools/Stirling-PDF · warning · Error

policy requires interactive input and cannot run automatical

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.

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)

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.