moeru-ai/airi · error · Error

Invalid duration format: ${duration}

Error message

Invalid duration format: ${duration}

What it means

Thrown by parseWindowDuration() in the temporal perception detector. It parses a duration string into milliseconds using the regex /^(\d+(?:\.\d+)?)(ms|s|m)?$/. Supported formats: bare number (treated as ms), 'NNms', 'NNs', 'NNm'. Anything that does not match — letters without a number, unsupported units (h, d), spaces, negative numbers, multiple units — throws with the offending input echoed.

Source

Thrown at integrations/minecraft/src/cognitive/perception/rules/temporal-detector.ts:229

export function processEvent(
  state: DetectorState,
  input: ProcessDetectorInput,
): readonly [boolean, DetectorState] {
  if (input.mode === 'tumbling') {
    return processTumblingEvent(state, input)
  }

  return processSlidingEvent(state, input)
}

/**
 * Parse window duration string to milliseconds
 * Supports: '2s', '500ms', '1m', '100'
 */
export function parseWindowDuration(duration: string): number {
  const match = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m)?$/)
  if (!match) {
    throw new Error(`Invalid duration format: ${duration}`)
  }

  const value = Number.parseFloat(match[1])
  const unit = match[2] || 'ms'

  switch (unit) {
    case 'ms': return value
    case 's': return value * 1000
    case 'm': return value * 60 * 1000
    default: return value
  }
}

/**
 * Calculate number of slots for a given window duration
 */
export function calculateWindowSlots(
  windowMs: number,

View on GitHub (pinned to 27111382b4)

Solutions

  1. Use one of the supported formats: '500ms', '2s', '1m', or a bare number (milliseconds).
  2. Normalise user/config input to the supported units before calling parseWindowDuration.
  3. If you need hours/days, convert to minutes or milliseconds upstream (e.g. 2h -> 120m).
  4. Trim whitespace and validate non-empty before parsing.

Example fix

// before
//   parseWindowDuration('1h')    // throws
//   parseWindowDuration('5 s')   // throws
//
// after
//   parseWindowDuration('60m')   // ok -> 3600000
//   parseWindowDuration('5s')    // ok -> 5000
Defensive patterns

Strategy: validation

Validate before calling

import { parseWindowDuration } from './temporal-detector'

const DURATION_RE = /^(\d+(?:\.\d+)?)(ms|s|m)?$/
export function safeParseDuration(input: string): number | null {
  const s = input.trim()
  if (!DURATION_RE.test(s)) return null
  return parseWindowDuration(s)
}

// const ms = safeParseDuration(userInput)
// if (ms === null) { /* reject the config value */ }

Type guard

function isDurationString(v: unknown): v is string {
  return typeof v === 'string' && /^\d+(?:\.d+)?(?:ms|s|m)?$/.test(v.trim())
}

Prevention

When it happens

Trigger: Passing '1h', '2d', '-5s', '5 seconds', 'abc', '5s5', '5 s', or a value with a unit the regex does not recognise; passing an empty string; passing a value with a trailing space or newline that breaks the anchored regex.

Common situations: Config typo in a detector rule window; user-supplied duration from a chat command parsed without normalisation; unit confusion (caller assumes '100' means seconds but the default unit is ms); copying a duration from a tool that uses 'h'/'d'.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/ec31696bc8331dd5. Report an issue: GitHub.