JuliusBrussee/caveman · error

maxLoadedTools must be at least the alwaysLoad tool count

Error message

maxLoadedTools must be at least the alwaysLoad tool count

What it means

When the catalog contains `alwaysLoad` tools, `maxLoadedTools` must be large enough to hold all of them; otherwise the invariant that always-load tools are always resident cannot be honored. The library refuses the configuration rather than silently dropping always-load tools.

Solutions

  1. Raise maxLoadedTools to at least the alwaysLoad tool count
  2. Remove alwaysLoad flags from tools that can be lazily loaded
  3. Filter the catalog to fewer alwaysLoad tools before passing it

Example fix

// before
cave.tools({ catalog, maxLoadedTools: 3 }); // catalog has 5 alwaysLoad tools
// after
const alwaysCount = catalog.filter(t => t.alwaysLoad).length;
cave.tools({ catalog, maxLoadedTools: Math.max(3, alwaysCount) });
Defensive patterns

Strategy: validation

Validate before calling

const alwaysCount = catalog.filter(t => t.alwaysLoad).length;
if (cfg.maxLoadedTools !== undefined && cfg.maxLoadedTools < alwaysCount) {
  cfg.maxLoadedTools = alwaysCount;
}

Type guard

const canFitAlwaysLoad = (max: number, catalog: {alwaysLoad?: boolean}[]): boolean =>
  catalog.filter(t => t.alwaysLoad).length <= max;

Try / catch

try {
  return cave.tools({ catalog, maxLoadedTools });
} catch (e) {
  if (e.message.includes("at least the alwaysLoad tool count")) {
    return cave.tools({ catalog, maxLoadedTools: catalog.filter(t => t.alwaysLoad).length });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `tools()` where `catalog.filter(t => t.alwaysLoad).length > maxLoadedTools`, e.g. 5 alwaysLoad tools with maxLoadedTools: 3 (and strategy is not "all").

Common situations: Growing the set of alwaysLoad tools after setting a tight budget; lowering maxLoadedTools for cost control without re-counting alwaysLoad entries.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/9e33008b943e79e9. Report an issue: GitHub.

Appendix: source

Thrown at packages/sdk/typescript/src/index.ts:486

  tools(config: { catalog: CaveTool[]; strategy?: "all" | "deferred"; initialToolCount?: number; maxLoadedTools?: number }) {
    const strategy = config.strategy ?? "all";
    if (strategy !== "all" && strategy !== "deferred") {
      throw new Error("strategy must be all or deferred");
    }
    const initialToolCount = config.initialToolCount ?? 8;
    if (!Number.isSafeInteger(initialToolCount) || initialToolCount < 0) {
      throw new Error("initialToolCount must be a non-negative integer");
    }
    if (config.maxLoadedTools !== undefined && (!Number.isSafeInteger(config.maxLoadedTools) || config.maxLoadedTools < 1)) {
      throw new Error("maxLoadedTools must be a positive integer");
    }
    let initial: CaveTool[];
    if (strategy === "all") {
      initial = config.catalog.slice();
    } else {
      const always = config.catalog.filter((tool) => tool.alwaysLoad);
      if (config.maxLoadedTools !== undefined && always.length > config.maxLoadedTools) {
        throw new Error("maxLoadedTools must be at least the alwaysLoad tool count");
      }
      const lazy = config.catalog.filter((tool) => !tool.alwaysLoad);
      const lazyLimit = config.maxLoadedTools === undefined
        ? initialToolCount
        : Math.min(initialToolCount, config.maxLoadedTools - always.length);
      initial = always.concat(lazy.slice(0, lazyLimit));
    }
    const cave = this;

    return {
      strategy,
      initial,
      /**
       * Search the full catalog via the gateway's /sdk/v1/tool-search endpoint.
       * Returns a ToolSearchResult with the reduced tool list and token counts.
       *
       * @param query   The user's current intent / task description.
       * @param options.maxTools Optional cap on returned tools.

View on GitHub (pinned to 3ee70a1026)