JuliusBrussee/caveman · error · Error

caveman agent: subagent maxTokens must be a positive integer

Error message

caveman agent: subagent maxTokens must be a positive integer

What it means

Thrown by the subagent tool factory when options.maxTokens is present but not a safe positive integer. maxTokens optionally caps the subagent model's output tokens; because it is optional, passing undefined skips the check, but any supplied value must be a whole number >= 1 (NaN, 0, negatives, floats, and numeric strings all throw).

Source

Thrown at packages/agent/src/index.ts:182

  maxTokens?: number;
  maxContextTokens?: number;
}): ToolDefinition {
  const maxInputChars = options.maxInputChars ?? 32_768;
  if (!Number.isSafeInteger(maxInputChars) || maxInputChars <= 0) {
    throw new Error("caveman agent: subagent maxInputChars must be a positive integer");
  }
  const maxCalls = options.maxCalls ?? 1;
  const maxCostUsd = options.maxCostUsd ?? 1;
  const maxContextTokens = options.maxContextTokens ?? 128_000;
  if (!Number.isSafeInteger(maxCalls) || maxCalls <= 0) {
    throw new Error("caveman agent: subagent maxCalls must be a positive integer");
  }
  if (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {
    throw new Error("caveman agent: subagent maxCostUsd must be positive");
  }
  if (options.maxTokens !== undefined &&
      (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0)) {
    throw new Error("caveman agent: subagent maxTokens must be a positive integer");
  }
  if (!Number.isSafeInteger(maxContextTokens) || maxContextTokens <= 0) {
    throw new Error("caveman agent: subagent maxContextTokens must be a positive integer");
  }
  return tool({
    name: options.name,
    description: options.description,
    input: schema.object({ task: schema.string() }),
    effect: "read",
    result: "auto",
    ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
    runtime: {
      kind: "subagent",
      definition: options.agent,
      maxInputChars,
      maxCalls,
      maxCostUsd,
      ...(options.maxTokens === undefined ? {} : { maxTokens: options.maxTokens }),

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass a positive safe integer, e.g. maxTokens: 8192
  2. If you do not need an output cap, omit the option entirely (undefined is accepted)
  3. Normalize optional config: maxTokens: cfg.maxTokens ?? undefined after checking it is a safe integer > 0
  4. Clamp floats: Math.max(1, Math.round(cfg.maxTokens))

Example fix

// before
subagentTool({ agent, name: "worker", maxTokens: cfg.max_tokens ?? 0 });

// after
subagentTool({
  agent,
  name: "worker",
  ...(Number.isSafeInteger(cfg.max_tokens) && cfg.max_tokens > 0
    ? { maxTokens: cfg.max_tokens }
    : {}),
});
Defensive patterns

Strategy: validation

Validate before calling

if (cfg.maxTokens !== undefined &&
    (!Number.isSafeInteger(cfg.maxTokens) || cfg.maxTokens <= 0)) {
  throw new Error(`maxTokens must be a positive integer when provided, got ${cfg.maxTokens}`);
}
subagentTool({ agent, name: "worker", ...(cfg.maxTokens === undefined ? {} : { maxTokens: cfg.maxTokens }) });

Type guard

const isOptionalPositiveInt = (v: unknown): v is number | undefined =>
  v === undefined || (Number.isSafeInteger(v) && v > 0);

Prevention

When it happens

Trigger: Explicitly passing maxTokens: 0, maxTokens: -1, maxTokens: 4096.5, maxTokens: NaN, or maxTokens: "4096". Unlike maxCalls/maxContextTokens there is no default — the error only fires when the option is defined and invalid.

Common situations: Forwarding a model-config object where maxTokens is optional and sometimes null/0, converting a provider's token limit (some providers accept -1 for 'default', this library does not), or typo'ing a numeric string.

Related errors


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