moeru-ai/airi · error · Error

The AR-HMM state count must be an integer greater than one.

Error message

The AR-HMM state count must be an integer greater than one.

What it means

`createArHmmModel()` fits a linear Gaussian AR-HMM and needs at least two hidden states to form a meaningful mixture (transitions require >=2 states). `options.stateCount` must be an integer >= 2; other values are rejected up front so EM does not run on a degenerate model.

Source

Thrown at packages/motion-driver-magic/src/ar-hmm.ts:358

    () => Array.from<number>({ length: options.stateCount }).fill(0),
  )
  for (let row = 0; row < rowCount - 1; row++) {
    const values = alpha[row].flatMap((value, state) => parameters.states.map(
      (_nextStateModel, nextState) => value + logTransitions[state][nextState] + emissions[row + 1][nextState] + beta[row + 1][nextState],
    ))
    const normalization = logSumExp(values)
    for (let state = 0; state < options.stateCount; state++) {
      for (let nextState = 0; nextState < options.stateCount; nextState++)
        transitionCounts[state][nextState] += Math.exp(values[state * options.stateCount + nextState] - normalization)
    }
  }
  return { gamma, transitionCounts, logLikelihood }
}

/** Creates a linear Gaussian AR-HMM model with deterministic clustering and EM updates. */
export function createArHmmModel(sequence: TrainingSequence, options: FitOptions): ArHmmModel {
  if (options.stateCount < 2 || !Number.isInteger(options.stateCount))
    throw new Error('The AR-HMM state count must be an integer greater than one.')
  if (options.iterations < 1 || !Number.isInteger(options.iterations))
    throw new Error('The AR-HMM iteration count must be a positive integer.')

  const sourceModel = fitVarParameters(sequence, {
    order: options.order,
    ridge: options.ridge,
  })
  const rowCount = sourceModel.trainingFrames.length - options.order
  if (rowCount < options.stateCount * (sourceModel.featureCount + 1))
    throw new Error('The current motion is too short for this AR-HMM shape.')

  const clusterFeatures = createClusterFeatures(sourceModel.trainingFrames, options.order)
  const assignments = initializeAssignments(clusterFeatures, options.stateCount)
  let expectation = createInitialExpectations(assignments, options.stateCount)
  let stateParameters = maximizeParameters(sourceModel, expectation, options)
  const logLikelihoods: number[] = []
  for (let iteration = 0; iteration < options.iterations; iteration++) {
    expectation = expectationStep(sourceModel, stateParameters, options)

View on GitHub (pinned to 9c213115f8)

Solutions

  1. Pass an integer `stateCount >= 2` in FitOptions.
  2. Clamp/round config-derived values: `Math.max(2, Math.round(rawStateCount))`.
  3. If only one behavior is expected, do not use AR-HMM; fit a single VAR model instead.
  4. Add schema validation (e.g. Valibot) on the options object before fitting.

Example fix

// before
createArHmmModel(seq, { stateCount: 1 })
// after
createArHmmModel(seq, { stateCount: Math.max(2, Math.round(rawStateCount)) })
Defensive patterns

Strategy: validation

Validate before calling

function isValidStateCount(n: unknown): n is number {
  return typeof n === 'number' && Number.isInteger(n) && n >= 2
}

Type guard

function isFitOptions(o: unknown): o is FitOptions {
  return typeof o === 'object' && o !== null
    && 'stateCount' in o && isValidStateCount((o as FitOptions).stateCount)
    && 'iterations' in o && isValidIterations((o as FitOptions).iterations)
}

Try / catch

try {
  const model = createArHmmModel(seq, options)
} catch (error) {
  if (error instanceof Error && error.message.includes('state count')) {
    return createArHmmModel(seq, { ...options, stateCount: 2 })
  }
  throw error
}

Prevention

When it happens

Trigger: Calling `createArHmmModel(seq, { stateCount: 0 })`, `{ stateCount: 1 }`, or a non-integer like 2.5. Also occurs when stateCount is derived from a computation (e.g. parsing user config or dividing) that yields a non-integer or <=1 value.

Common situations: Reading `stateCount` from a UI slider or config file without enforcing an integer minimum of 2; a default of 1 used as 'single behavior'; passing Math results like `totalStates / groups` that round to a non-integer.

Related errors


AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02). Data as JSON: /api/errors/304f8c08dbd907b3. Report an issue: GitHub.