{"record":{"id":"627ce1291ce7739d","repo":"mastra-ai/mastra","slug":"github-issue-reconcile-interval-must-be-a-positive","errorCode":null,"errorMessage":"GitHub issue reconcile interval must be a positive number.","messagePattern":"GitHub issue reconcile interval must be a positive number\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/github/reconcile-worker.ts","lineNumber":81,"sourceCode":"  #leaseProvider: LeaseProvider = NoopLeaseProvider;\n  #nextPullRequestReconcileAt = 0;\n  #nextIssueReconcileAt = 0;\n\n  constructor(config: GithubReconcileWorkerConfig) {\n    super();\n    if (!config.reconcile && !config.reconcileIssues) {\n      throw new Error('GitHub reconcile worker requires a pull request or issue reconciler.');\n    }\n    this.#reconcile = config.reconcile;\n    this.#reconcileIssues = config.reconcileIssues;\n    this.#sourceControl = config.sourceControl;\n    this.#intervalMs = config.intervalMs ?? DEFAULT_GITHUB_RECONCILE_INTERVAL_MS;\n    this.#issueIntervalMs = config.issueIntervalMs ?? this.#intervalMs;\n    if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n      throw new Error('GitHub pull request reconcile interval must be a positive number.');\n    }\n    if (!Number.isFinite(this.#issueIntervalMs) || this.#issueIntervalMs <= 0) {\n      throw new Error('GitHub issue reconcile interval must be a positive number.');\n    }\n    this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, Math.min(this.#intervalMs, this.#issueIntervalMs) * 3);\n    this.#now = config.now ?? Date.now;\n  }\n\n  async init(deps: WorkerDeps): Promise<void> {\n    await super.init(deps);\n    this.#leaseProvider = getLeaseProvider(deps.pubsub);\n  }\n\n  async start(): Promise<void> {\n    if (this.#running) return;\n    if (!this.deps) throw new Error('GithubReconcileWorker: call init() before start()');\n    this.#running = true;\n    this.deps.logger.info('GitHub reconcile worker started', {\n      pullRequestIntervalMs: this.#reconcile ? this.#intervalMs : undefined,\n      issueIntervalMs: this.#reconcileIssues ? this.#issueIntervalMs : undefined,\n    });","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/github/reconcile-worker.ts#L63-L99","documentation":"The GitHub reconcile worker constructor validates that both the pull-request interval (intervalMs) and the derived issue interval (issueIntervalMs, defaulting to intervalMs) are finite numbers greater than zero. It throws this error when issueIntervalMs is missing its default, NaN, Infinity, zero, or negative, because a non-positive interval would make the issue reconciliation scheduler fire immediately or never, corrupting lease TTL math (#leaseTtlMs = min(intervals)*3).","triggerScenarios":"Calling `new GithubReconcileWorker({ intervalMs: 0 })`, `intervalMs: -1000`, `intervalMs: NaN`, or explicitly passing `issueIntervalMs: 0` / `issueIntervalMs: -1` while intervalMs itself is valid (the PR-interval check passes but the issue check fails).","commonSituations":"Config loaded from env vars where GITHUB_ISSUE_RECONCILE_INTERVAL is unset-but-empty ('' parsed as invalid), a YAML/JSON config typo like `issueIntervalMs: -1`, or arithmetic that yields NaN (e.g. multiplying an undefined value) before constructing the worker.","solutions":["Pass a finite positive number for intervalMs (and issueIntervalMs if set), e.g. 60_000.","If relying on the default, omit issueIntervalMs entirely instead of passing null/0.","Sanitize env/config parsing: `const ms = Number(process.env.X); if (!Number.isFinite(ms) || ms <= 0) use default;`.","Catch the error at startup to fail fast with a clearer config-level message."],"exampleFix":"// before\nconst worker = new GithubReconcileWorker({ intervalMs: Number(env.RECONCILE_MS) }); // NaN when env unset\n// after\nconst ms = Number(env.RECONCILE_MS);\nconst worker = new GithubReconcileWorker({ intervalMs: Number.isFinite(ms) && ms > 0 ? ms : 60_000 });","handlingStrategy":"validation","validationCode":"function validInterval(ms) { return typeof ms === 'number' && Number.isFinite(ms) && ms > 0; }\nif (!validInterval(intervalMs) || (issueIntervalMs !== undefined && !validInterval(issueIntervalMs))) {\n  throw new TypeError('reconcile intervals must be finite positive numbers');\n}","typeGuard":"const isPositiveFinite = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v > 0;","tryCatchPattern":"try {\n  worker = new GithubReconcileWorker(config);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('must be a positive number')) {\n    throw new Error(`Invalid reconcile config: ${JSON.stringify(config)}`, { cause: err });\n  }\n  throw err;\n}","preventionTips":["Parse intervals through a single sanitize helper with a default fallback.","Never pass user/env strings directly as intervalMs; coerce with Number and validate.","Add a unit test asserting the constructor throws for 0, -1, and NaN."],"tags":["configuration","validation","github","scheduler"],"backgroundTag":"invalid-configuration-value","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}