{"record":{"id":"bd2d4ccb1457251d","repo":"hcengineering/platform","slug":"ticks-per-second-has-an-invalid-value-must-be","errorCode":null,"errorMessage":"Ticks per second has an invalid value: must be >= 1 && <= 1000","messagePattern":"Ticks per second has an invalid value: must be >= 1 && <= 1000","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"foundations/net/packages/core/src/utils.ts","lineNumber":36,"sourceCode":"}\n\n/**\n * Handles a time unification and inform about ticks.\n */\nexport class TickManagerImpl implements TickManager {\n  handlers = new Map<number, [TickHandler, number, number]>()\n\n  hashCounter: number = 0\n\n  _tick: number = 0\n\n  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    }","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/net/packages/core/src/utils.ts#L18-L54","documentation":"The tick/timer utility validates its tps (ticks per second) in the constructor and rejects any value outside the 1–1000 inclusive range. A tps above 1000 would require sub-millisecond timers the runtime cannot honor, and values below 1 are meaningless. The constructor fails fast so an invalid clock is never used.","triggerScenarios":"Constructing the tick utility with tps = 0, a negative number, NaN-adjacent values comparing oddly, or a value > 1000 (e.g. passing milliseconds instead of seconds-per-tick, or multiplying tps by 1000 by mistake).","commonSituations":"Confusing tps with interval milliseconds (1000 used as '1 second'); loading tps from config/env where an empty or malformed string yields a bad number; defaulting logic producing 0 when a config key is missing.","solutions":["Pass a tps between 1 and 1000 inclusive (e.g. new Ticker(60)).","Sanitize config: Number.parseInt the env value and clamp it with Math.min(1000, Math.max(1, value)).","Provide a sane default (e.g. 60) when the config value is missing or invalid instead of passing 0/undefined.","If you need sub-millisecond resolution, this utility cannot provide it; redesign the timing loop instead of raising tps."],"exampleFix":"// before\nconst ticker = new Ticker(Number(process.env.TPS)) // undefined -> invalid\n// after\nconst tps = Math.min(1000, Math.max(1, Number(process.env.TPS ?? 60)))\nconst ticker = new Ticker(tps)","handlingStrategy":"validation","validationCode":"function parseTps(raw: unknown, fallback = 60): number {\n  const n = typeof raw === 'number' ? raw : Number(raw)\n  if (!Number.isFinite(n) || n < 1 || n > 1000) return fallback\n  return Math.floor(n)\n}\nconst ticker = new Ticker(parseTps(process.env.TPS))","typeGuard":"function isValidTps(v: unknown): v is number {\n  return typeof v === 'number' && Number.isFinite(v) && Number.isInteger(v) && v >= 1 && v <= 1000\n}","tryCatchPattern":"let ticker: Ticker\ntry {\n  ticker = new Ticker(configuredTps)\n} catch (e) {\n  if ((e as Error).message.includes('Ticks per second')) {\n    console.warn(`invalid tps ${configuredTps}, falling back to 60`)\n    ticker = new Ticker(60)\n  } else throw e\n}","preventionTips":["Clamp tps from config/env with Math.min(1000, Math.max(1, value)).","Provide a documented default (e.g. 60) and validate config at boot.","Unit-test config parsing so empty/NaN values never reach the constructor.","Remember tps is ticks per second — never pass millisecond durations as tps."],"tags":["validation","constructor","timer","configuration"],"backgroundTag":"invalid-parameter-value","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}