mastra-ai/mastra · error · Error

${fieldPath} must be a non-negative number of milliseconds o

Error message

${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".

What it means

parseActivationTTL validates TTL options (e.g. activateAfterIdle / bufferActivation TTLs) at construction. A number must be a finite, non-negative millisecond value; anything else (negative, NaN, Infinity) throws. This fails fast on invalid durations instead of producing broken timers.

Source

Thrown at packages/memory/src/processors/observational-memory/observational-memory.ts:183

}

export { didProviderChange } from './model-context';

function parseActivationTTL(
  value: number | string | false | undefined,
  fieldPath: string,
): number | 'auto' | undefined {
  if (value === undefined || value === false) {
    return undefined;
  }

  if (value === 'auto') {
    return value;
  }

  if (typeof value === 'number') {
    if (!Number.isFinite(value) || value < 0) {
      throw new Error(`${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m".`);
    }
    return value;
  }

  const trimmed = value.trim();
  const match = trimmed.match(
    /^(\d+(?:\.\d+)?)\s*(ms|msec|msecs|millisecond|milliseconds|s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours)$/i,
  );

  if (!match) {
    throw new Error(
      `${fieldPath} must be a non-negative number of milliseconds or a duration string like "5m" or "1hr".`,
    );
  }

  const rawAmount = match[1]!;
  const rawUnit = match[2]!;
  const amount = Number(rawAmount);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Use a non-negative finite number of milliseconds (e.g. 300000 for 5 minutes)
  2. Use a duration string like "5m", "30s", or "1hr" instead of raw numbers
  3. Clamp computed values: Math.max(0, computedMs) and validate Number.isFinite before passing

Example fix

// before
new ObservationalMemory({ model, activateAfterIdle: Date.now() - startTime }) // negative
// after
const ttl = Math.max(0, Date.now() - startTime);
new ObservationalMemory({ model, activateAfterIdle: ttl });
Defensive patterns

Strategy: validation

Validate before calling

function assertTtlMs(v: number, field: string): void {
  if (!Number.isFinite(v) || v < 0) {
    throw new Error(`${field} must be a non-negative number of milliseconds or a duration string like "5m".`);
  }
}
assertTtlMs(config.activateAfterIdle, 'activateAfterIdle');

Type guard

function isValidTtlNumber(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
}

Try / catch

try {
  const mem = new ObservationalMemory(config);
} catch (err) {
  if (err instanceof Error && err.message.includes('must be a non-negative number of milliseconds')) {
    config.activateAfterIdle = 300000; // sane default: 5m
  } else throw err;
}

Prevention

When it happens

Trigger: Passing a negative number, NaN, or Infinity as a numeric TTL option (e.g. activateAfterIdle: -1) when constructing ObservationalMemory.

Common situations: Computing TTL from a subtraction that went negative (Date.now() - laterTimestamp); math with undefined producing NaN; imported config where Infinity was used as 'never expire'.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/80fbc79cefed53b7. Report an issue: GitHub.