JuliusBrussee/caveman · error
caveman agent: invalid agent id
Error message
caveman agent: invalid agent id ${JSON.stringify(options.id)} What it means
The agent() definition factory validates the agent id against /^[a-z0-9][a-z0-9_-]{0,95}$/ and throws with a JSON-stringified copy of the invalid id. Ids must start with a lowercase letter or digit, contain only lowercase alphanumerics, underscores, and hyphens, and be at most 96 characters.
Solutions
- Slugify the id: lowercase, replace invalid characters with '-' or '_'
- Strip leading characters that are not [a-z0-9] and truncate to 96 chars
- Validate the id with the same regex in your own code before calling agent()
- Use a stable kebab-case identifier instead of a display name
Example fix
// before
agent({ id: "My Research Agent", ... });
// after
const id = "My Research Agent".toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+/, "").slice(0, 96);
agent({ id, ... }); Defensive patterns
Strategy: validation
Validate before calling
const AGENT_ID_RE = /^[a-z0-9][a-z0-9_-]{0,95}$/;
if (!AGENT_ID_RE.test(id)) throw new Error(`invalid agent id: ${id}`); Type guard
const isValidAgentId = (id) => typeof id === "string" && /^[a-z0-9][a-z0-9_-]{0,95}$/.test(id); Try / catch
try { def = agent({ id, ...rest }); } catch (err) { if (String(err.message).startsWith("caveman agent: invalid agent id")) { def = agent({ id: slugify(id), ...rest }); } else throw err; } Prevention
- Slugify display names into ids at the boundary
- Never derive ids from user-visible labels directly
- Keep ids kebab-case and <= 96 chars by convention
When it happens
Trigger: agent({ id: 'My Agent' }) (uppercase/space); empty string; id starting with '-' or '_'; ids longer than 96 chars; ids with dots or unicode characters.
Common situations: Deriving ids from human-readable project names with spaces/camelCase; using UUIDs with uppercase; templating ids from environment names like 'Prod.EU'.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- cave_agent_definition_invalid
- cave_subagent_definition_cycle
- cave_subagent_depth_limit
- caveman agent: duplicate tool name
- caveman agent: invalid memory namespace
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/3903753d33d38d96.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/index.ts:99
const SANDBOX_MODES: readonly AgentDefinition["sandbox"][] = [
"required",
"fixture",
"host",
];
export function agent(options: {
id: string;
instructions: string | FileSource;
model: Auto | string | Model<Api>;
reasoning?: AgentDefinition["reasoning"];
tools?: ToolDefinition[];
contexts?: ContextDefinition[];
memory?: MemoryDefinition;
output?: OutputDefinition;
sandbox?: AgentDefinition["sandbox"];
}): AgentDefinition {
if (!/^[a-z0-9][a-z0-9_-]{0,95}$/.test(options.id)) {
throw new Error(`caveman agent: invalid agent id ${JSON.stringify(options.id)}`);
}
const tools = Object.freeze([...(options.tools ?? [])]);
if (new Set(tools.map((item) => item.name)).size !== tools.length) {
throw new Error("caveman agent: duplicate tool name");
}
const sandbox = options.sandbox ?? "required";
if (!SANDBOX_MODES.includes(sandbox)) {
throw new Error(`caveman agent: unknown sandbox mode ${JSON.stringify(sandbox)}`);
}
const reserved = tools.find((item) => item.name.startsWith("cave_"));
if (reserved) {
throw new Error(
`caveman agent: tool prefix cave_ is reserved by framework (${reserved.name})`,
);
}
const definition: AgentDefinition = {
kind: "agent",
id: options.id,View on GitHub (pinned to 3ee70a1026)