JuliusBrussee/caveman · error · Error

caveman agent: context ttlTurns must be a positive integer

Error message

caveman agent: context ttlTurns must be a positive integer

What it means

Thrown by the context() builder when the optional ttlTurns is present but is not a safe integer greater than zero. ttlTurns bounds how many conversation turns a context segment stays alive, so 0, negatives, fractions, and non-numbers are invalid; omitting the field entirely means no turn-based expiry.

Source

Thrown at packages/agent/src/primitives.ts:350

  readonly ttlTurns?: number;
}

export function context(options: {
  id: string;
  kind: ContextKind;
  source: string | FileSource;
  stability: ContextStability;
  safety?: SafetyClass;
  priority?: ContextPriority;
  recovery?: RecoveryKind;
  cacheRegion?: CacheRegion;
  privacy?: PrivacyClass;
  opaque?: boolean;
  ttlTurns?: number;
}): ContextDefinition {
  if (options.id.trim() === "") throw new Error("caveman agent: context id is required");
  if (options.ttlTurns !== undefined && (!Number.isSafeInteger(options.ttlTurns) || options.ttlTurns <= 0)) {
    throw new Error("caveman agent: context ttlTurns must be a positive integer");
  }
  const definition: ContextDefinition = {
    kind: "context",
    id: options.id,
    segmentKind: options.kind,
    source: options.source,
    stability: options.stability,
    safety: options.safety ?? "S0",
    priority: options.priority ?? "required",
    recovery: options.recovery ?? "none",
    cacheRegion: options.cacheRegion ?? (options.stability === "build" ? "frozen_prefix" : "live_zone"),
    privacy: options.privacy ?? "local_sensitive",
    opaque: options.opaque ?? false,
    ...(options.ttlTurns === undefined ? {} : { ttlTurns: options.ttlTurns }),
  };
  return Object.freeze(definition);
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass a positive integer (e.g. ttlTurns: 10) or omit ttlTurns when the segment should live for the whole run
  2. Round computed values: ttlTurns: Math.max(1, Math.ceil(derived))
  3. Coerce config values with Number() and validate Number.isSafeInteger before calling context()

Example fix

// before
context({ id: 'scratch', kind: 'episodic', source: note, stability: 'volatile', ttlTurns: totalTurns / 4 });

// after
context({ id: 'scratch', kind: 'episodic', source: note, stability: 'volatile', ttlTurns: Math.max(1, Math.ceil(totalTurns / 4)) });
Defensive patterns

Strategy: validation

Validate before calling

function toTtlTurns(raw: unknown): number | undefined {
  if (raw === undefined) return undefined;
  const n = typeof raw === 'number' ? raw : Number(raw);
  if (!Number.isSafeInteger(n) || n <= 0) throw new Error(`ttlTurns must be a positive integer, got ${String(raw)}`);
  return n;
}

Type guard

function isTtlTurns(value: unknown): value is number { return typeof value === 'number' && Number.isSafeInteger(value) && value > 0; }

Prevention

When it happens

Trigger: Calling context({ ttlTurns: 0 }), ttlTurns: -3, ttlTurns: 2.5, ttlTurns: NaN, or ttlTurns beyond 2^53-1.

Common situations: Computing ttlTurns from a ratio (totalTurns / 4) and passing the fraction; using 0 to mean 'no limit' (the correct no-limit is to omit the option); config strings not coerced to numbers.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/8301edad241dfd05. Report an issue: GitHub.