JuliusBrussee/caveman · error · Error

cave_untrusted_tool_definition:${declared.name}

Error message

cave_untrusted_tool_definition:${declared.name}

What it means

Thrown by validateAgentGraph (packages/agent/src/definition-graph.ts:46): a tool definition lacks the TOOLS_IMPLEMENTATION_SOURCE marker (the Symbol.for('@caveman-ai/agent:tool-implementation-source') string property). Only tools created by the framework's own tool()/subagent() factories carry that symbol, so its absence means the object was hand-forged rather than produced by the trusted builder API.

Source

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

    }
    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);
}

/**
 * True when any agent in the graph opts into host mode.
 *
 * Lock eligibility is a property of the whole graph, not of its root: a host
 * subagent runs its tool closures in the host process just as a host root does,
 * so its evidence shows no containment either. Follows the same subagent edges

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Create every tool with the exported tool() (and subagents with subagent()) from @caveman-ai/agent instead of object literals.
  2. If definitions must cross a process/serialization boundary, re-create tools via the factory on the receiving side rather than deserializing tool objects.
  3. Check for duplicate installations of @caveman-ai/agent (Symbol.for is cross-realm, but a factory that stops setting the symbol indicates a version mismatch) and align versions.

Example fix

// before
const myTool = { kind: "tool", name: "search", /* ... */ } as ToolDefinition;

// after
import { tool } from "@caveman-ai/agent";
const myTool = tool({ name: "search", /* ... */ });
Defensive patterns

Strategy: type-guard

Validate before calling

import { tool } from "@caveman-ai/agent";
// Every tool in the array must come from the factory — verify by construction:
function assertFactoryTools(tools: unknown[]): void {
  for (const t of tools) {
    if (typeof Reflect.get(Object(t), Symbol.for("@caveman-ai/agent:tool-implementation-source")) !== "string") {
      throw new Error(`tool '${(t as { name?: string }).name}' was not created via tool()`);
    }
  }
}

Type guard

const TOOL_SRC = Symbol.for("@caveman-ai/agent:tool-implementation-source");
function isFactoryTool(value: unknown): value is ToolDefinition {
  return typeof value === "object" && value !== null &&
    typeof Reflect.get(value, TOOL_SRC) === "string";
}

Prevention

When it happens

Trigger: Passing a plain object literal (or an object spread/cloned from a real tool) in AgentDefinition.tools and then running validateAgentGraph, compile, or the dev/build CLI. Structured cloning, JSON round-trips, or {...realTool} spreads drop non-enumerable/symbol properties and also trigger it.

Common situations: Hand-assembling a ToolDefinition to bypass the factory; serializing/deserializing tool definitions; deep-cloning a tool with a spread or a cloner that drops symbol-keyed properties; constructing tools with a different (incompatible) version of the package than the one validating the graph.

Related errors


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