JuliusBrussee/caveman · error

cave_sandbox_agent_export_missing

cave_sandbox_agent_export_missing

Error message

cave_sandbox_agent_export_missing

What it means

Thrown by the cave sandbox tool worker (packages/agent/src/tool-worker.ts) after it dynamically imports the entry module from the request and finds no usable agent definition. The worker expects `imported.default ?? imported.agent` to be an object with `kind === "agent"`. If both exports are missing, undefined, or not marked kind:"agent", the run fails closed before any tool executes.

Source

Thrown at packages/agent/src/tool-worker.ts:106

  const request = await readRequest();
  if (typeof request.entry !== "string" || !Array.isArray(request.agentPath) ||
      request.agentPath.length > 8 ||
      request.agentPath.some((item) => typeof item !== "string" ||
        !/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(item)) ||
      typeof request.rootDefinitionSha256 !== "string" ||
      !/^[a-f0-9]{64}$/.test(request.rootDefinitionSha256) ||
      typeof request.toolDefinitionSha256 !== "string" ||
      !/^[a-f0-9]{64}$/.test(request.toolDefinitionSha256) ||
      typeof request.tool !== "string" ||
      !/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(request.tool) ||
      typeof request.allowSideEffects !== "boolean" ||
      typeof request.allowNetwork !== "boolean") {
    throw new Error("cave_sandbox_request_invalid");
  }
  if (request.allowNetwork !== true) installNetworkDeny();
  const imported = await import(request.entry) as { default?: AgentDefinition; agent?: AgentDefinition };
  let definition = imported.default ?? imported.agent;
  if (!definition || definition.kind !== "agent") throw new Error("cave_sandbox_agent_export_missing");
  validateAgentGraph(definition);
  if (agentDefinitionSHA256(definition) !== request.rootDefinitionSha256) {
    throw new Error("cave_sandbox_definition_mismatch");
  }
  const visited = new Set<AgentDefinition>([definition]);
  for (const name of request.agentPath) {
    const delegated = definition.tools.filter((item) =>
      item.name === name && item.runtime?.kind === "subagent"
    );
    if (delegated.length !== 1) throw new Error("cave_sandbox_unknown_subagent");
    const child = delegated[0]!.runtime!.definition as AgentDefinition;
    if (!child || child.kind !== "agent") {
      throw new Error("cave_sandbox_subagent_definition_invalid");
    }
    if (visited.has(child)) throw new Error("cave_sandbox_subagent_cycle");
    visited.add(child);
    definition = child;
  }

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Ensure the entry module has `export default defineAgent(...)` (or `export const agent = ...`) producing an object with kind:"agent".
  2. Verify request.entry resolves to the exact file that owns the agent definition, not an index/barrel.
  3. Rebuild the package (`pnpm --dir packages/agent build`) so the worker imports current output rather than a stale artifact.
  4. Check the import succeeds at module top level — a throw during module init surfaces as this or an uncaught code, so run `node -e "import('./entry').then(m=>console.log(Object.keys(m)))"` to inspect exports.

Example fix

// before
export const researchAgent = { tools: [...] }; // no kind, no default export

// after
import { agent } from "@caveman-ai/agent";
export default agent({ tools: [...] }); // default export, kind: "agent"
Defensive patterns

Strategy: validation

Validate before calling

async function assertAgentEntry(entry: string): Promise<void> {
  const mod = (await import(entry)) as { default?: unknown; agent?: unknown };
  const def = mod.default ?? mod.agent;
  if (!def || (def as { kind?: string }).kind !== "agent") {
    throw new Error(`entry ${entry} lacks a default/agent export with kind "agent"`);
  }
}

Type guard

function isAgentDefinition(v: unknown): v is { kind: "agent"; tools: unknown[] } {
  return !!v && typeof v === "object" && (v as { kind?: unknown }).kind === "agent";
}

Try / catch

try { await import(entry); } catch (e) { /* treat any module-shape failure as fatal; do not spawn worker with a suspect entry */ }

Prevention

When it happens

Trigger: The parent process spawns the worker with request.entry pointing at a module that (a) exports nothing, (b) exports the agent under a different name (e.g. `export const myAgent`), (c) default-exports a plain object/tool without `kind: "agent"`, or (d) fails module initialization so the import resolves to an incomplete namespace.

Common situations: Refactoring an agent file and renaming its export; a bundled/compiled entry whose default export shape changed between versions; an entry path resolved to a barrel file that re-exports the agent non-default; a stale build artifact from `pnpm build` that no longer matches source.

Related errors


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