hcengineering/platform · error

Ticks must be >= 1

Error message

Ticks must be >= 1

What it means

waitTick resolves when the internal tick counter reaches the current tick plus `ticks`; it requires at least 1 tick of waiting. Zero or negative values have no future target tick, so the library rejects them immediately instead of resolving spuriously or hanging.

Source

Thrown at foundations/net/packages/core/src/utils.ts:111

    }
    this.started = true
    const to = setInterval(
      () => {
        this.tick().catch((err) => {
          console.error('Error in tick manager:', err)
        })
      },
      Math.round(1000 / this.tps)
    )
    this.stop = () => {
      this.started = false
      clearInterval(to)
    }
  }

  async waitTick (ticks: number): Promise<void> {
    if (ticks < 1) {
      throw new Error('Ticks must be >= 1')
    }
    const targetTick = this._tick + ticks

    await new Promise<void>((resolve) => {
      this.tickListeners.set(targetTick, [...(this.tickListeners.get(targetTick) ?? []), resolve])
    })
  }
}

export function composeCID (prefix: string, id: string): ContainerUuid {
  return `${prefix}_${id}` as ContainerUuid
}

export class FakeTickManager implements TickManager {
  private currentTime: number = 0
  private readonly handlers: Array<{ handler: TickHandler, interval: number, lastTick: number }> = []

  now = (): number => {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Pass an integer >= 1, or skip the call entirely when no wait is needed: if (ticks >= 1) await ticker.waitTick(ticks).
  2. Clamp computed values: Math.max(1, computedTicks) when a wait is mandatory.
  3. Ensure the quantity is measured in ticks, not seconds/milliseconds; convert explicitly if needed.
  4. Audit the call site that produced 0 and handle the no-op case before awaiting.

Example fix

// before
await ticker.waitTick(batchCount) // batchCount can be 0
// after
if (batchCount >= 1) await ticker.waitTick(batchCount)
Defensive patterns

Strategy: validation

Validate before calling

async function safeWaitTick(ticker: Ticker, ticks: number): Promise<void> {
  if (!Number.isInteger(ticks) || ticks < 1) return // nothing to wait for
  await ticker.waitTick(ticks)
}

Type guard

function isPositiveTickCount(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && v >= 1
}

Try / catch

try {
  await ticker.waitTick(ticks)
} catch (e) {
  if ((e as Error).message === 'Ticks must be >= 1') {
    console.warn(`waitTick(${ticks}) invalid — skipping wait`)
  } else throw e
}

Prevention

When it happens

Trigger: Calling await ticker.waitTick(0) or waitTick(negative) — typically computed values like waitTick(someCounter) where the counter was 0, or waitTick(end - start) with end <= start.

Common situations: Passing a computed delay that evaluated to 0 (e.g. ticks = Math.floor(elapsed) with elapsed < 1); unit confusion passing seconds and expecting auto-conversion; loop logic where an empty batch yields 0 ticks.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/7dc0f9f95f6b306e. Report an issue: GitHub.