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
- Pass an integer `stateCount >= 2` in FitOptions.
- Clamp/round config-derived values: `Math.max(2, Math.round(rawStateCount))`.
- If only one behavior is expected, do not use AR-HMM; fit a single VAR model instead.
- 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
- Clamp config-driven stateCount with Math.max(2, Math.round(x)).
- Validate options with a schema (Valibot) before fitting.
- Never hardcode stateCount: 1 for AR-HMM — use a plain VAR fit instead.
- Watch for division producing non-integers when deriving stateCount.
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
- The AR-HMM iteration count must be a positive integer.
- The current motion is too short for this AR-HMM shape.
- The motion sample rate must be positive.
- The VAR order must be a positive integer.
- mutexAcquireTimeout must be a positive finite number
AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02).
Data as JSON: /api/errors/304f8c08dbd907b3.
Report an issue: GitHub.