paperclipai/paperclip · error

Runner live rotation requires a valid generated-at time

Error message

Runner live rotation requires a valid generated-at time

What it means

runnerLiveRotationWeek converts a generated-at timestamp into a stable rotation index (0-6) used to pick which live adapter candidates run this week. It requires Date.parse to yield a finite epoch value; an unparseable or missing timestamp string throws this error, since rotation cannot be computed deterministically without a valid instant.

Source

Thrown at packages/paperclip-runner/src/eval/live-workflow-matrix.ts:30

export const RUNNER_LIVE_SCHEDULE_SCHEMA =
  "paperclip.runner.live-eval-schedule.v1" as const;

export function parseRunnerLiveCampaignCostLimit(
  value: string | undefined,
): number {
  const parsed = Number(value ?? "12");
  if (!Number.isFinite(parsed) || parsed <= 0) {
    throw new Error(
      "PAPERCLIP_EVAL_MAX_CAMPAIGN_COST_USD must be a positive finite number",
    );
  }
  return parsed;
}

export function runnerLiveRotationWeek(generatedAt: string): number {
  const epochMs = Date.parse(generatedAt);
  if (!Number.isFinite(epochMs)) {
    throw new Error("Runner live rotation requires a valid generated-at time");
  }
  const epochWeek = Math.floor(epochMs / (7 * 86_400_000));
  return ((epochWeek % 7) + 7) % 7;
}

export type RunnerLiveAdapter =
  "codex_app_server" | "opencode_server" | "acpx_runtime";
export type RunnerLiveTier = "strong" | "inexpensive";

export interface RunnerLiveEvalCandidate {
  schema: typeof RUNNER_LIVE_CANDIDATE_SCHEMA;
  id: string;
  slotId: string;
  adapter: RunnerLiveAdapter;
  provider: RunnerWorkflowProvider;
  model: string;
  tier: RunnerLiveTier;
  reasoningEffort?: string;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Use a valid ISO 8601 UTC timestamp for generatedAt, e.g. new Date().toISOString() produces '2026-09-02T12:00:00.000Z'.
  2. Regenerate the schedule manifest with the tooling that produces it instead of hand-editing the timestamp field.
  3. Check for unsubstituted template placeholders or empty strings in the manifest's generatedAt field.
  4. If parsing external input, validate with Number.isFinite(Date.parse(v)) before calling and surface the raw value in the failure.

Example fix

// before
runnerLiveRotationWeek("09/02/2026 12:00 PM")
// after
runnerLiveRotationWeek(new Date().toISOString()) // "2026-09-02T12:00:00.000Z"
Defensive patterns

Strategy: validation

Validate before calling

function isValidGeneratedAt(v: string): boolean {
  return typeof v === "string" && v.length > 0 && Number.isFinite(Date.parse(v));
}
// usage: if (!isValidGeneratedAt(schedule.generatedAt)) throw new Error(`bad generatedAt: ${JSON.stringify(schedule.generatedAt)}`);

Type guard

function isParsableDate(v: unknown): v is string {
  return typeof v === "string" && v.trim() !== "" && Number.isFinite(Date.parse(v));
}

Try / catch

try {
  const week = runnerLiveRotationWeek(schedule.generatedAt);
} catch (error) {
  if (String(error).includes("valid generated-at")) {
    console.error(`schedule.generatedAt is not a parseable date: ${JSON.stringify(schedule.generatedAt)}; regenerate the manifest`);
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling runnerLiveRotationWeek with a string Date.parse cannot parse: an empty string, a non-ISO formatted date, a localized date, 'undefined'/'null' as literal strings, or a truncated timestamp from a hand-edited schedule manifest (RUNNER_LIVE_SCHEDULE_SCHEMA) — called from first/second rotation selection paths.

Common situations: Hand-editing a live-eval schedule JSON and mistyping generatedAt; generating the manifest with a non-ISO8601 date format; a template leaving ${generatedAt} unsubstituted; timezone-localized strings that V8's Date.parse rejects in some runtimes.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-02). Data as JSON: /api/errors/c7d43e15c014e000. Report an issue: GitHub.