{"record":{"id":"a5e23f1786a0b2b4","repo":"hcengineering/platform","slug":"interval-must-be-a-finite-number-1-seconds","errorCode":null,"errorMessage":"Interval must be a finite number >= 1 (seconds)","messagePattern":"Interval must be a finite number >= 1 \\(seconds\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"foundations/net/packages/core/src/utils.ts","lineNumber":48,"sourceCode":"  tickListeners = new Map<number, (() => void)[]>()\n\n  started: boolean = false\n\n  constructor (readonly tps: number) {\n    if (tps > 1000 || tps < 1) {\n      throw new Error('Ticks per second has an invalid value: must be >= 1 && <= 1000')\n    }\n  }\n\n  now (): number {\n    // Use performance.now() when available, otherwise fall back to Date.now()\n    // performance is available in recent Node versions, but guard for portability.\n    return (globalThis as any).performance?.now?.() ?? Date.now()\n  }\n\n  register (handler: TickHandler, interval: number): () => void {\n    if (!Number.isFinite(interval) || interval < 1) {\n      throw new Error('Interval must be a finite number >= 1 (seconds)')\n    }\n    const handlerId = this.hashCounter++\n    this.handlers.set(handlerId, [handler, handlerId % this.tps, interval])\n    return () => {\n      this.handlers.delete(handlerId)\n    }\n  }\n\n  async tick (): Promise<void> {\n    this._tick++\n\n    // Handle tick listeners\n    if (this.tickListeners.size > 0) {\n      const listeners = this.tickListeners.get(this._tick) ?? []\n      this.tickListeners.delete(this._tick)\n      for (const listener of listeners) {\n        try {\n          listener()","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/net/packages/core/src/utils.ts#L30-L66","documentation":"register schedules a TickHandler to fire every `interval` seconds, so the interval must be a finite number >= 1. NaN, Infinity, 0, negatives, and fractional sub-second intervals are rejected at registration time. It returns an unregister function only when validation passes.","triggerScenarios":"Calling ticker.register(handler, interval) with interval = 0, a negative number, NaN (e.g. from parseInt of a bad env var), or Infinity — all of which fail the Number.isFinite/interval < 1 check.","commonSituations":"Env/config strings like '' or 'abc' converting to NaN; mistaking milliseconds for seconds (500 intended as 0.5s); uninitialized variable defaulting to undefined→NaN; unit confusion after migrating from a ms-based timer API.","solutions":["Pass a whole number of seconds >= 1 (e.g. register(handler, 5)).","Convert from milliseconds explicitly: Math.ceil(ms / 1000), and guard against < 1.","Sanitize the source value: Number.isFinite(v) ? v : fallback before calling register.","If you need sub-second granularity, use setInterval directly or raise tps instead of passing fractional intervals."],"exampleFix":"// before\nticker.register(handler, Number(process.env.INTERVAL_MS)) // NaN or ms-as-seconds\n// after\nconst secs = Math.max(1, Math.ceil(Number(process.env.INTERVAL_MS ?? 1000) / 1000))\nticker.register(handler, secs)","handlingStrategy":"validation","validationCode":"function toIntervalSeconds(raw: unknown, fallback = 5): number {\n  const n = typeof raw === 'number' ? raw : Number(raw)\n  if (!Number.isFinite(n)) return fallback\n  return Math.max(1, Math.ceil(n)) // supports ms callers via explicit conversion upstream\n}\nticker.register(handler, toIntervalSeconds(process.env.HANDLER_INTERVAL_S))","typeGuard":"function isValidInterval(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v) && v >= 1\n}","tryCatchPattern":"try {\n  const cancel = ticker.register(handler, interval)\n  registered.push(cancel)\n} catch (e) {\n  if ((e as Error).message.includes('Interval must be')) {\n    console.warn(`invalid interval ${interval}, using 5s`)\n    registered.push(ticker.register(handler, 5))\n  } else throw e\n}","preventionTips":["Convert milliseconds to seconds explicitly (Math.ceil(ms/1000)) when migrating from ms-based timers.","Sanitize env/config numbers with Number.isFinite before use.","Never pass 0 or fractional values; the minimum interval is 1 second by design.","Keep a named constant for default intervals instead of inlining raw numbers from config."],"tags":["validation","timer","interval","configuration"],"backgroundTag":"invalid-parameter-value","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}