JuliusBrussee/caveman · error

cave_tool_definition_invalid

cave_tool_definition_invalid

Error message

cave_tool_definition_invalid

What it means

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.

Solutions

  1. Ensure every tools entry is an object with kind: "tool" and a string name.
  2. Use the library's tool creation helper to build definitions rather than hand-writing plain objects.
  3. Filter or assert the tools array at construction time to drop undefined/null entries.
  4. Check imports — mixing tool shapes from incompatible library versions triggers this.

Example fix

// before
const root = { kind: "agent", tools: [searchFn] };
// after
const root = { kind: "agent", tools: [{ kind: "tool", name: "search", /* ... */ }] };
Defensive patterns

Strategy: validation

Validate before calling

function isValidToolDeclaration(t: unknown): t is ToolDefinition {
  return typeof t === "object" && t !== null &&
    (t as ToolDefinition).kind === "tool" &&
    typeof (t as ToolDefinition).name === "string";
}
definition.tools.forEach((t, i) => { if (!isValidToolDeclaration(t)) throw new Error(`tools[${i}] is not a valid tool declaration`); });

Type guard

function isToolDefinition(v: unknown): v is ToolDefinition {
  return typeof v === "object" && v !== null &&
    (v as ToolDefinition).kind === "tool" &&
    typeof (v as ToolDefinition).name === "string";
}

Try / catch

try {
  validateAgentGraph(def);
} catch (e) {
  if (e instanceof Error && e.message === "cave_tool_definition_invalid") {
    console.error("Every entry in tools must be an object with kind: 'tool' and a string name");
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at packages/agent/src/definition-graph.ts:38

      throw new Error("cave_agent_definition_invalid");
    }
    if (depth > 8) throw new Error("cave_subagent_depth_limit");
    if (active.has(definition)) throw new Error("cave_subagent_definition_cycle");
    // Host mode is an opt-in the root makes for itself. A descendant cannot use
    // it to run closures outside an ancestor's required containment.
    if (sandboxRequired && definition.sandbox === "host") {
      throw new Error("cave_host_sandbox_nested_under_required");
    }
    const memo = visited[sandboxRequired ? 1 : 0]!;
    if (memo.has(definition)) return;
    active.add(definition);
    const childSandboxRequired = sandboxRequired ||
      definition.sandbox === "required";
    const names = new Set<string>();
    for (const declared of definition.tools) {
      if (!declared || declared.kind !== "tool" ||
          typeof declared.name !== "string") {
        throw new Error("cave_tool_definition_invalid");
      }
      if (names.has(declared.name)) throw new Error("cave_duplicate_tool_name");
      names.add(declared.name);
      if (declared.name.startsWith("cave_")) {
        throw new Error(`cave_reserved_tool_name:${declared.name}`);
      }
      if (typeof Reflect.get(declared, TOOL_IMPLEMENTATION_SOURCE) !== "string") {
        throw new Error(`cave_untrusted_tool_definition:${declared.name}`);
      }
      if (declared.runtime?.kind !== "subagent") continue;
      const child = declared.runtime.definition as AgentDefinition;
      visit(child, depth + 1, childSandboxRequired);
    }
    active.delete(definition);
    memo.add(definition);
  };

  visit(root, 0, false);

View on GitHub (pinned to 3ee70a1026)