JuliusBrussee/caveman · error

cave_agent_definition_invalid

cave_agent_definition_invalid

Error message

cave_agent_definition_invalid

What it means

Thrown by the visit closure in validateAgentGraph when an AgentDefinition is null, has kind !== "agent", or its tools field is not an array. It is the entry-point shape validation for the agent definition graph — the definition object is malformed before any deeper structural checks run.

Solutions

  1. Ensure the definition has kind: "agent" and tools as an array (use [] if there are no tools).
  2. Check that you are passing the AgentDefinition object itself, not a wrapper (e.g. { agent: def }).
  3. Log/inspect the definition passed in; null usually means a failed lookup or import.
  4. If definitions come from JSON config, validate them against the AgentDefinition schema before loading.

Example fix

// before
validateAgentGraph({ name: "root", tools: myTool });
// after
validateAgentGraph({ kind: "agent", name: "root", tools: [myTool] });
Defensive patterns

Strategy: validation

Validate before calling

function assertAgentDefinition(def: unknown): asserts def is AgentDefinition {
  if (!def || (def as AgentDefinition).kind !== "agent" || !Array.isArray((def as AgentDefinition).tools)) {
    throw new Error("definition must have kind: 'agent' and a tools array");
  }
}

Type guard

function isAgentDefinition(v: unknown): v is AgentDefinition {
  return typeof v === "object" && v !== null &&
    (v as AgentDefinition).kind === "agent" &&
    Array.isArray((v as AgentDefinition).tools);
}

Try / catch

try {
  validateAgentGraph(definition);
} catch (e) {
  if (e instanceof Error && e.message === "cave_agent_definition_invalid") {
    console.error("Agent definition malformed: needs kind === 'agent' and tools: []");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateAgentGraph (directly or through agent construction/run) with a definition that is undefined/null, an object whose kind is missing or not "agent", or one where tools was omitted, set to null, or set to a non-array value.

Common situations: Hand-written agent JSON missing the kind field, a typo like kinds: "agent", tools accidentally assigned a single tool object instead of an array, or a deserialized definition that lost fields during a version migration.

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/d0f457a5d1185db3. Report an issue: GitHub.

Appendix: source

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

const TOOL_IMPLEMENTATION_SOURCE = Symbol.for(
  "@caveman-ai/agent:tool-implementation-source",
);

export function validateAgentGraph(root: AgentDefinition): void {
  // Memoize per inherited containment posture: the same child definition
  // reached under a sandbox-required ancestor must be re-checked, not skipped.
  const visited = [new Set<AgentDefinition>(), new Set<AgentDefinition>()];
  const active = new Set<AgentDefinition>();

  const visit = (
    definition: AgentDefinition,
    depth: number,
    sandboxRequired: boolean,
  ): void => {
    if (!definition || definition.kind !== "agent" ||
        !Array.isArray(definition.tools)) {
      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");

View on GitHub (pinned to 3ee70a1026)