paperclipai/paperclip · error · Error

Invalid migrator producer response.

Error message

Invalid migrator producer response.

What it means

After a successful HTTP response, migratorPublished() validates the GitHub workflow-runs payload shape: workflow_runs must be an array, total_count a safe non-negative integer, and on page 1 zero total_count must correspond to zero listed runs. Anything else throws this error. It guards against unexpected API response shapes.

Solutions

  1. Print the raw response body to see what actually came back (proxy HTML vs JSON).
  2. Bypass proxies/VPNs that inject 200 HTML responses and retry.
  3. Pin an explicit X-GitHub-Api-Version header and check for GitHub API changelog updates affecting the actions/workflows/runs endpoint.
  4. If using a test double for fetchImpl, make it return { total_count, workflow_runs } consistent with each other.

Example fix

// before
const body = await response.json(); // assumed valid
// after
const body = await response.json();
if (!Array.isArray(body.workflow_runs)) console.error("Unexpected body:", body);
Defensive patterns

Strategy: type-guard

Validate before calling

const res = await fetch(url, opts);
const text = await res.text();
let body; try { body = JSON.parse(text); } catch { throw new Error("Non-JSON body (proxy?): " + text.slice(0, 120)); }

Type guard

const looksLikeRunList = (b) => b && Array.isArray(b.workflow_runs) && Number.isSafeInteger(b.total_count) && b.total_count >= 0;

Try / catch

try {
  await waitForCloudArtifacts(sha);
} catch (error) {
  if (error.message === "Invalid migrator producer response.") {
    console.error("GitHub returned an unexpected payload — check proxies/GitHub status.");
  } else throw error;
}

Prevention

When it happens

Trigger: GitHub returns 200 with a malformed/JSON body missing workflow_runs or total_count (e.g. an HTML error page behind a captive proxy, a changed API contract, or total_count===0 with runs listed or vice versa).

Common situations: Corporate proxies or API gateways rewriting responses; GitHub API preview/contract changes; intermediary auth portals returning 200 HTML; mocking layers returning incomplete fixtures.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/2490586d9f1639df. Report an issue: GitHub.

Appendix: source

Thrown at scripts/cloud-readiness.mjs:24

import path from "node:path";
import { imageExists, versionFor } from "./preview-artifacts.mjs";
import { verifyPublished } from "./cloud-migrator-artifacts.mjs";

const repository = "paperclipai/paperclip";
const workflow = ".github/workflows/cloud-migrator-artifacts.yml";

export async function migratorPublished(sha, fetchImpl, token) {
  let pending = false;
  const failures = [];
  for (let page = 1; page <= 10; page++) {
    const response = await fetchImpl(`https://api.github.com/repos/${repository}/actions/workflows/cloud-migrator-artifacts.yml/runs?branch=master&head_sha=${sha}&per_page=100&page=${page}`, {
      headers: { Accept: "application/vnd.github+json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
      redirect: "error", signal: AbortSignal.timeout(30_000),
    });
    if (!response.ok) throw new Error(`Migrator producer lookup failed: HTTP ${response.status}`);
    const body = await response.json();
    if (!Array.isArray(body.workflow_runs) || !Number.isSafeInteger(body.total_count) || body.total_count < 0 ||
        (page === 1 && (body.total_count === 0) !== (body.workflow_runs.length === 0))) throw new Error("Invalid migrator producer response.");
    if (body.total_count === 0) return false;
    for (const run of body.workflow_runs) {
      if (run.head_sha !== sha || run.head_branch !== "master" || run.path !== workflow ||
          run.head_repository?.id !== 1170821064 || run.head_repository.full_name !== repository ||
          !["push", "workflow_dispatch"].includes(run.event)) throw new Error("Migrator producer identity mismatch.");
      // Publication is immutable. A later failed manual run must not hide a
      // successful exact-source publisher; the signed bundle is checked next.
      if (run.status === "completed" && run.conclusion === "success") return true;
      if (run.status !== "completed") pending = true;
      else failures.push(`${run.id}: ${run.conclusion}`);
    }
    if (page * 100 >= body.total_count) {
      if (pending) return false;
      throw new Error(`Migrator producers failed: ${failures.join(", ")}.`);
    }
  }
  throw new Error("Too many migrator producer runs to establish publication.");
}

View on GitHub (pinned to 3f1d897a7c)