JuliusBrussee/caveman · error

caveman agent: entry must export default agent()

Error message

caveman agent: entry must export default agent()

What it means

agentFromImported in packages/agent/src/cli.ts loads the entry module given to 'caveman agent' and accepts either a default export or a named `agent` export that must have kind === 'agent'. If the module has neither export, the export is undefined/null, or the object is not an agent definition, it throws 'caveman agent: entry must export default agent()'.

Solutions

  1. Add `export default { kind: "agent", ... }` to the entry file, or export it as `export const agent = { kind: "agent", ... }`.
  2. Verify the exported object includes kind: "agent" (not a factory function returning it).
  3. Confirm you passed the correct entry file path to `caveman agent` — one that actually defines the agent.
  4. Log the module's keys (`Object.keys(await import(path))`) to see what is actually exported after transpilation.
  5. If using a bundler/TS config, ensure the export is preserved (e.g. no tree-shaking of the default export).

Example fix

// before
const helper = { kind: "tool", name: "x" };
export { helper };
// after
const agent = { kind: "agent", name: "my-agent", tools: [helper] };
export default agent;
Defensive patterns

Strategy: type-guard

Validate before calling

const mod = await import(entryPath);
if (!(mod.default ?? mod.agent)) throw new Error(`Entry ${entryPath} has no default/agent export`);

Type guard

const isAgentDefinition = (x: unknown): x is AgentDefinition =>
  typeof x === "object" && x !== null && (x as any).kind === "agent";

Try / catch

try {
  await runAgent(entryPath);
} catch (e) {
  if (e.message.includes("must export default agent()")) {
    console.error(`Entry ${entryPath} must ` + "export default { kind: 'agent', ... }");
  } else throw e;
}

Prevention

When it happens

Trigger: Running `caveman agent <path>` where the entry file has no default export and no `agent` export, exports something that is not an AgentDefinition (e.g. a plain object without kind: 'agent', or a function), or exports the agent under a different name.

Common situations: Authoring an agent file with only `export const myAgent = {...}`; forgetting `export default`; using `export function agent()` (a function, not an object with kind 'agent'); transpilation/bundler output stripping or renaming the export; pointing caveman at a utility module instead of the agent entry.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at packages/agent/src/cli.ts:1383

  const configured = [
    process.env.ANTHROPIC_API_KEY && "anthropic/claude-haiku-4-5",
    process.env.OPENAI_API_KEY && "openai/gpt-5.4-mini",
    (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) && "google/gemini-2.5-flash",
  ].filter((value): value is string => typeof value === "string");
  if (configured.length !== 1) {
    throw new Error("caveman build: set CAVE_MODEL when zero or multiple provider credentials exist");
  }
  return configured[0]!;
}

async function loadAgent(path: string): Promise<AgentDefinition> {
  return agentFromImported(await importFresh(path));
}

function agentFromImported(imported: unknown): AgentDefinition {
  const exported = imported as { default?: AgentDefinition; agent?: AgentDefinition };
  const definition = exported.default ?? exported.agent;
  if (!definition || definition.kind !== "agent") throw new Error("caveman agent: entry must export default agent()");
  return definition;
}

async function readLock(root: string): Promise<CaveBuildLock> {
  return parseCaveBuildLock(JSON.parse(await readFile(resolve(root, ".caveman/agent.lock.json"), "utf8")));
}

function importFresh(path: string): Promise<unknown> {
  return import(`${pathToFileURL(path).href}?cave=${Date.now()}-${crypto.randomUUID()}`);
}

function isEval(value: unknown): value is EvalDefinition {
  return value !== null && typeof value === "object" && (value as { kind?: unknown }).kind === "eval";
}

function fixtureInput(value: unknown): string {
  return typeof value === "string" ? value : JSON.stringify(value);
}

View on GitHub (pinned to 3ee70a1026)