mastra-ai/mastra · error

GitHub pull request reconcile interval must be a positive nu

Error message

GitHub pull request reconcile interval must be a positive number.

What it means

The constructor validates that intervalMs is a finite number greater than zero before scheduling pull request reconciliation. NaN, 0, negative, or Infinity intervals would break timers and lease math, so construction fails immediately.

Source

Thrown at mastracode/factory/src/integrations/github/reconcile-worker.ts:78

  #running = false;
  #timer: ReturnType<typeof setTimeout> | undefined;
  #inFlight: Promise<void> | undefined;
  #leaseProvider: LeaseProvider = NoopLeaseProvider;
  #nextPullRequestReconcileAt = 0;
  #nextIssueReconcileAt = 0;

  constructor(config: GithubReconcileWorkerConfig) {
    super();
    if (!config.reconcile && !config.reconcileIssues) {
      throw new Error('GitHub reconcile worker requires a pull request or issue reconciler.');
    }
    this.#reconcile = config.reconcile;
    this.#reconcileIssues = config.reconcileIssues;
    this.#sourceControl = config.sourceControl;
    this.#intervalMs = config.intervalMs ?? DEFAULT_GITHUB_RECONCILE_INTERVAL_MS;
    this.#issueIntervalMs = config.issueIntervalMs ?? this.#intervalMs;
    if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {
      throw new Error('GitHub pull request reconcile interval must be a positive number.');
    }
    if (!Number.isFinite(this.#issueIntervalMs) || this.#issueIntervalMs <= 0) {
      throw new Error('GitHub issue reconcile interval must be a positive number.');
    }
    this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, Math.min(this.#intervalMs, this.#issueIntervalMs) * 3);
    this.#now = config.now ?? Date.now;
  }

  async init(deps: WorkerDeps): Promise<void> {
    await super.init(deps);
    this.#leaseProvider = getLeaseProvider(deps.pubsub);
  }

  async start(): Promise<void> {
    if (this.#running) return;
    if (!this.deps) throw new Error('GithubReconcileWorker: call init() before start()');
    this.#running = true;
    this.deps.logger.info('GitHub reconcile worker started', {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass a finite positive interval in milliseconds, e.g. intervalMs: 60_000.
  2. Sanitize env-derived values: const ms = Number(raw); if (!Number.isFinite(ms) || ms <= 0) use default.
  3. Check for NaN produced by Number(undefined) and fix the defaulting logic.
  4. Omit intervalMs entirely to use DEFAULT_GITHUB_RECONCILE_INTERVAL_MS.

Example fix

// before
intervalMs: Number(process.env.RECONCILE_INTERVAL), // NaN when unset
// after
const parsed = Number(process.env.RECONCILE_INTERVAL);
intervalMs: Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
Defensive patterns

Strategy: validation

Validate before calling

const intervalMs = config.intervalMs;
if (intervalMs !== undefined && (!Number.isFinite(intervalMs) || intervalMs <= 0)) throw new Error(`invalid intervalMs: ${intervalMs}`);

Type guard

function isPositiveMs(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v > 0;
}

Try / catch

try {
  worker = new GithubReconcileWorker(config);
} catch (e) {
  if ((e as Error).message.includes('must be a positive number')) {
    log.error('bad reconcile interval, falling back to default');
    worker = new GithubReconcileWorker({ ...config, intervalMs: undefined });
  } else throw e;
}

Prevention

When it happens

Trigger: new GithubReconcileWorker({ reconcile, intervalMs: 0 }) — or intervalMs parsed from an env var string ('NaN'), a negative value, or Number(undefined) leaking NaN through a ?? that never triggers because a non-null invalid value was provided.

Common situations: intervalMs: Number(process.env.INTERVAL) where the env var is unset or non-numeric (NaN passes ?? defaults); specifying milliseconds as seconds; Infinity from a division bug.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/422dd0ee62aa541b. Report an issue: GitHub.