mastra-ai/mastra · error · Error

${config.integrationId} issue reconcile interval must be a p

Error message

${config.integrationId} issue reconcile interval must be a positive number.

What it means

The IssueReconcileWorker constructor validates its intervalMs configuration before the worker is scheduled. The interval must be a finite positive number since it drives setInterval-like scheduling and lease TTL derivation (leaseTtlMs = max(MIN_LEASE_TTL_MS, intervalMs * 3)). A zero, negative, NaN, or Infinity interval would break scheduling or lease expiry math, so construction fails immediately with the integration ID in the message.

Source

Thrown at mastracode/factory/src/integrations/issue-reconcile-worker.ts:42

  readonly #intervalMs: number;
  readonly #leaseTtlMs: number;
  readonly #leaseKey: string;
  readonly #leaseOwner = randomUUID();

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

  constructor(config: IssueReconcileWorkerConfig) {
    super();
    this.#integrationId = config.integrationId;
    this.name = `${config.integrationId}-issue-reconcile`;
    this.#leaseKey = `${config.integrationId}:issue-reconcile`;
    this.#reconcile = config.reconcile;
    this.#intervalMs = config.intervalMs ?? DEFAULT_ISSUE_RECONCILE_INTERVAL_MS;
    if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {
      throw new Error(`${config.integrationId} issue reconcile interval must be a positive number.`);
    }
    this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);
  }

  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('IssueReconcileWorker: call init() before start()');
    this.#running = true;
    this.deps.logger.info(`${this.#integrationId} issue reconcile worker started`, { intervalMs: this.#intervalMs });
    this.#schedule(0);
  }

  async stop(): Promise<void> {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Fix the config source so intervalMs is a positive finite number in milliseconds (e.g. 300000 for 5 minutes)
  2. Sanitize at the config boundary: reject/replace non-finite or <= 0 values before constructing the worker
  3. If the value comes from an env var, validate with Number.isFinite(v) && v > 0 and fall back to DEFAULT_ISSUE_RECONCILE_INTERVAL_MS
  4. If 0 was intended to mean 'disabled', use an explicit enabled flag instead of encoding it in intervalMs

Example fix

// before
const worker = new IssueReconcileWorker({
  integrationId: 'github',
  reconcile,
  intervalMs: Number(process.env.ISSUE_RECONCILE_INTERVAL_MS), // '' → 0 → throws
});
// after
const raw = Number(process.env.ISSUE_RECONCILE_INTERVAL_MS);
const intervalMs = Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_ISSUE_RECONCILE_INTERVAL_MS;
const worker = new IssueReconcileWorker({ integrationId: 'github', reconcile, intervalMs });
Defensive patterns

Strategy: validation

Validate before calling

const raw = Number(config.intervalMs);
if (!Number.isFinite(raw) || raw <= 0) {
  throw new Error(`intervalMs must be a positive finite number (got ${config.intervalMs})`);
}
const worker = new IssueReconcileWorker({ ...config, intervalMs: raw });

Type guard

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

Try / catch

try {
  worker = new IssueReconcileWorker(config);
} catch (err) {
  if ((err as Error).message.includes('reconcile interval must be a positive number')) {
    logger.error('Bad ISSUE_RECONCILE_INTERVAL_MS config, using default', { raw: config.intervalMs });
    worker = new IssueReconcileWorker({ ...config, intervalMs: DEFAULT_ISSUE_RECONCILE_INTERVAL_MS });
  } else throw err;
}

Prevention

When it happens

Trigger: Constructing new IssueReconcileWorker({ integrationId, reconcile, intervalMs }) where intervalMs is 0, a negative number, NaN, or Infinity — usually because it came from parsed env config or a JSON config that was missing/invalid.

Common situations: Env var like ISSUE_RECONCILE_INTERVAL_MS parsed with Number('') → 0 or Number('abc') → NaN; config file has "intervalMs": null and the ?? fallback doesn't apply because null was explicitly set (note: nullish coalescing does catch null, but 0 passes through); unit mismatch (seconds given where ms expected → value like 30 treated as effectively zero-lease); copied default with 0 meaning 'disabled' which is not supported.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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