{"record":{"id":"ca05f7c116a9c47e","repo":"JuliusBrussee/caveman","slug":"cave-tool-definition-invalid","errorCode":"cave_tool_definition_invalid","errorMessage":"cave_tool_definition_invalid","messagePattern":"cave_tool_definition_invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/definition-graph.ts","lineNumber":38,"sourceCode":"      throw new Error(\"cave_agent_definition_invalid\");\n    }\n    if (depth > 8) throw new Error(\"cave_subagent_depth_limit\");\n    if (active.has(definition)) throw new Error(\"cave_subagent_definition_cycle\");\n    // Host mode is an opt-in the root makes for itself. A descendant cannot use\n    // it to run closures outside an ancestor's required containment.\n    if (sandboxRequired && definition.sandbox === \"host\") {\n      throw new Error(\"cave_host_sandbox_nested_under_required\");\n    }\n    const memo = visited[sandboxRequired ? 1 : 0]!;\n    if (memo.has(definition)) return;\n    active.add(definition);\n    const childSandboxRequired = sandboxRequired ||\n      definition.sandbox === \"required\";\n    const names = new Set<string>();\n    for (const declared of definition.tools) {\n      if (!declared || declared.kind !== \"tool\" ||\n          typeof declared.name !== \"string\") {\n        throw new Error(\"cave_tool_definition_invalid\");\n      }\n      if (names.has(declared.name)) throw new Error(\"cave_duplicate_tool_name\");\n      names.add(declared.name);\n      if (declared.name.startsWith(\"cave_\")) {\n        throw new Error(`cave_reserved_tool_name:${declared.name}`);\n      }\n      if (typeof Reflect.get(declared, TOOL_IMPLEMENTATION_SOURCE) !== \"string\") {\n        throw new Error(`cave_untrusted_tool_definition:${declared.name}`);\n      }\n      if (declared.runtime?.kind !== \"subagent\") continue;\n      const child = declared.runtime.definition as AgentDefinition;\n      visit(child, depth + 1, childSandboxRequired);\n    }\n    active.delete(definition);\n    memo.add(definition);\n  };\n\n  visit(root, 0, false);","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/definition-graph.ts#L20-L56","documentation":"Thrown by visit in validateAgentGraph when an entry in an agent's tools array is not a valid tool declaration: it is null, its kind !== \"tool\", or its name is not a string. This validates each declared tool's basic shape before name/uniqueness/trust checks.","triggerScenarios":"Passing a raw function instead of a tool definition object, an object with kind omitted or misspelled, a tool built by a helper returning undefined on failure, or tools: [null] from a failed lookup.","commonSituations":"Refactoring tool factories so they sometimes return plain objects, importing tools from another library with a different shape, typos like type: \"tool\" instead of kind: \"tool\", or conditional spreads that drop required fields.","solutions":["Ensure every tools entry is an object with kind: \"tool\" and a string name.","Use the library's tool creation helper to build definitions rather than hand-writing plain objects.","Filter or assert the tools array at construction time to drop undefined/null entries.","Check imports — mixing tool shapes from incompatible library versions triggers this."],"exampleFix":"// before\nconst root = { kind: \"agent\", tools: [searchFn] };\n// after\nconst root = { kind: \"agent\", tools: [{ kind: \"tool\", name: \"search\", /* ... */ }] };","handlingStrategy":"validation","validationCode":"function isValidToolDeclaration(t: unknown): t is ToolDefinition {\n  return typeof t === \"object\" && t !== null &&\n    (t as ToolDefinition).kind === \"tool\" &&\n    typeof (t as ToolDefinition).name === \"string\";\n}\ndefinition.tools.forEach((t, i) => { if (!isValidToolDeclaration(t)) throw new Error(`tools[${i}] is not a valid tool declaration`); });","typeGuard":"function isToolDefinition(v: unknown): v is ToolDefinition {\n  return typeof v === \"object\" && v !== null &&\n    (v as ToolDefinition).kind === \"tool\" &&\n    typeof (v as ToolDefinition).name === \"string\";\n}","tryCatchPattern":"try {\n  validateAgentGraph(def);\n} catch (e) {\n  if (e instanceof Error && e.message === \"cave_tool_definition_invalid\") {\n    console.error(\"Every entry in tools must be an object with kind: 'tool' and a string name\");\n  } else throw e;\n}","preventionTips":["Build tools only via the library's tool factory helpers.","Filter tools arrays (tools.filter(isToolDefinition)) before composing agents.","Beware conditional spreads and failed factory calls that yield undefined entries."],"tags":["validation","tool-definition","schema"],"backgroundTag":"schema-validation-failed","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"}