{"record":{"id":"e169980f749d0609","repo":"JuliusBrussee/caveman","slug":"maxloadedtools-must-be-a-positive-integer","errorCode":null,"errorMessage":"maxLoadedTools must be a positive integer","messagePattern":"maxLoadedTools must be a positive integer","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/index.ts","lineNumber":478,"sourceCode":"   *\n   * strategy \"all\"      → initial = whole catalog; search() still calls the server.\n   * strategy \"deferred\" → initial = alwaysLoad tools (subset sent on first turn);\n   *                       search() calls the server to load relevant tools on demand.\n   *\n   * Breaking change from 1.0: search() is now async and returns ToolSearchResult\n   * (previously sync, returning CaveTool[]). Callers must await search().\n   */\n  tools(config: { catalog: CaveTool[]; strategy?: \"all\" | \"deferred\"; initialToolCount?: number; maxLoadedTools?: number }) {\n    const strategy = config.strategy ?? \"all\";\n    if (strategy !== \"all\" && strategy !== \"deferred\") {\n      throw new Error(\"strategy must be all or deferred\");\n    }\n    const initialToolCount = config.initialToolCount ?? 8;\n    if (!Number.isSafeInteger(initialToolCount) || initialToolCount < 0) {\n      throw new Error(\"initialToolCount must be a non-negative integer\");\n    }\n    if (config.maxLoadedTools !== undefined && (!Number.isSafeInteger(config.maxLoadedTools) || config.maxLoadedTools < 1)) {\n      throw new Error(\"maxLoadedTools must be a positive integer\");\n    }\n    let initial: CaveTool[];\n    if (strategy === \"all\") {\n      initial = config.catalog.slice();\n    } else {\n      const always = config.catalog.filter((tool) => tool.alwaysLoad);\n      if (config.maxLoadedTools !== undefined && always.length > config.maxLoadedTools) {\n        throw new Error(\"maxLoadedTools must be at least the alwaysLoad tool count\");\n      }\n      const lazy = config.catalog.filter((tool) => !tool.alwaysLoad);\n      const lazyLimit = config.maxLoadedTools === undefined\n        ? initialToolCount\n        : Math.min(initialToolCount, config.maxLoadedTools - always.length);\n      initial = always.concat(lazy.slice(0, lazyLimit));\n    }\n    const cave = this;\n\n    return {","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/typescript/src/index.ts#L460-L496","documentation":"The `tools()` factory validates `config.maxLoadedTools` at construction time. It must be a positive integer (>= 1), and `undefined` is allowed to fall back to `initialToolCount`-driven behavior. This fails fast so a bad budget doesn't silently break lazy tool loading later.","triggerScenarios":"Calling `tools({ catalog, maxLoadedTools: 0 })`, a negative number, a non-integer like 2.5, or NaN/Infinity (all fail `Number.isSafeInteger`).","commonSituations":"Computing the limit from user config or env parsing that yields 0, a float after division, or an unparsed NaN string.","solutions":["Ensure maxLoadedTools is a positive safe integer before calling tools()","Omit maxLoadedTools entirely to rely on initialToolCount","Clamp/round the value: Math.max(1, Math.round(n))"],"exampleFix":"// before\nconst toolsApi = cave.tools({ catalog, maxLoadedTools: Number(opts.limit) });\n// after\nconst limit = Number(opts.limit);\nconst maxLoadedTools = Number.isSafeInteger(limit) && limit >= 1 ? limit : undefined;\nconst toolsApi = cave.tools({ catalog, maxLoadedTools });","handlingStrategy":"validation","validationCode":"function validMaxLoadedTools(n) {\n  return n === undefined || (Number.isSafeInteger(n) && n >= 1);\n}\nif (!validMaxLoadedTools(cfg.maxLoadedTools)) throw new Error(\"maxLoadedTools must be a positive integer\");","typeGuard":"const isPositiveInt = (v: unknown): v is number =>\n  typeof v === \"number\" && Number.isSafeInteger(v) && v >= 1;","tryCatchPattern":"try {\n  const api = cave.tools({ catalog, maxLoadedTools });\n} catch (e) {\n  if (e.message.includes(\"maxLoadedTools must be a positive integer\")) {\n    return cave.tools({ catalog }); // fall back to defaults\n  }\n  throw e;\n}","preventionTips":["Validate numeric config from env/CLI with a schema (zod etc.) before constructing","Use Math.max(1, Math.round(x)) when deriving limits from computed values","Omit the option instead of passing 0/null"],"tags":["typescript","configuration","validation","tool-loading"],"backgroundTag":"invalid-argument-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}