JuliusBrussee/caveman · error · Error
cave_execution_authorization_ambiguous
cave_execution_authorization_ambiguous
Error message
cave_execution_authorization_ambiguous
What it means
Execution authorization is ambiguous when both a locked build (lockedBuild) and a candidate plan (candidatePlan) are supplied: the runtime cannot decide whether to execute a compiled/locked identity or search over a candidate. One and only one execution authorization may be present, so the run fails closed at start.
Solutions
- Remove one of the two fields — supply either lockedBuild (locked execution) or candidatePlan (candidate search), never both.
- Audit option-object merging/spreads so a default candidatePlan cannot survive when a lockedBuild is set.
- If migrating from candidate runs to a locked build, drop candidatePlan explicitly (set to undefined, not just leave in the object when using explicit property checks).
Example fix
// before
await runAgentInternal(def, input, { lockedBuild, candidatePlan, ...rest });
// after
await runAgentInternal(def, input,
lockedBuild !== undefined
? { lockedBuild, ...rest }
: { candidatePlan, ...rest }); Defensive patterns
Strategy: validation
Validate before calling
if (opts.lockedBuild !== undefined && opts.candidatePlan !== undefined) {
throw new Error("lockedBuild and candidatePlan are mutually exclusive");
} Type guard
function hasSingleExecutionAuthorization(o) {
return !(o.lockedBuild !== undefined && o.candidatePlan !== undefined);
} Try / catch
try {
await runAgentInternal(def, input, opts);
} catch (err) {
if (err.message === "cave_execution_authorization_ambiguous") {
await runAgentInternal(def, input, { ...opts, candidatePlan: undefined });
} else throw err;
} Prevention
- Build options objects conditionally: locked build XOR candidate plan.
- Avoid spreading multiple config layers that each carry an authorization field.
- Explicitly set fields to undefined when a later layer supersedes them.
When it happens
Trigger: Passing both options.lockedBuild and options.candidatePlan in InternalRunOptions to streamAgent/runAgent in the same call.
Common situations: Internal compiler/CLI code paths merging option objects where one layer adds a lockedBuild while a default carries candidatePlan; accidental spread of two config objects into one options bag.
Related errors
- cave_budget_conflicting_cap
- cave_budget_controller_without_budget
- cave_subagent_concurrency_limit_invalid
- cave_subagent_depth_limit_invalid
- cave_subagent_invocation_limit_invalid
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/8f534bb3e091d28d.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/runtime.ts:1070
async function* streamAgentInternal(
definition: AgentDefinition,
input: string,
options: InternalRunOptions,
executionContext: InternalExecutionContext,
): AsyncGenerator<CavemanRunEvent> {
if (options.maxSubagentInvocations !== undefined &&
(!Number.isSafeInteger(options.maxSubagentInvocations) || options.maxSubagentInvocations <= 0 ||
options.maxSubagentInvocations > ABSOLUTE_SUBAGENT_INVOCATION_LIMIT)) {
throw new Error("cave_subagent_invocation_limit_invalid");
}
if (options.maxConcurrentSubagents !== undefined &&
(!Number.isSafeInteger(options.maxConcurrentSubagents) || options.maxConcurrentSubagents <= 0 ||
options.maxConcurrentSubagents > ABSOLUTE_SUBAGENT_INVOCATION_LIMIT)) {
throw new Error("cave_subagent_concurrency_limit_invalid");
}
if (options.lockedBuild !== undefined && options.candidatePlan !== undefined) {
throw new Error("cave_execution_authorization_ambiguous");
}
// Budget shape is settled before anything else happens: an ambiguous or
// unbounded budget must fail at run() start, not after the first dollar.
// maxCostUsd and budget are two different contracts for the same money —
// one terminates with an error, the other returns a planned partial result —
// so carrying both would leave the run's own stop semantics undecided.
if (options.budget !== undefined && options.maxCostUsd !== undefined) {
throw new Error("cave_budget_conflicting_cap");
}
const budgetMeter = executionContext.budgetMeter ?? (options.budget === undefined
? undefined
: new BudgetMeter(normalizeRunBudget(options.budget)));
if (options.deadlineMs !== undefined &&
(!Number.isSafeInteger(options.deadlineMs) || options.deadlineMs <= 0)) {
throw new Error("cave_run_deadline_invalid");
}
if (options.maxSubagentDepth !== undefined &&
(!Number.isSafeInteger(options.maxSubagentDepth) || options.maxSubagentDepth <= 0 ||View on GitHub (pinned to 3ee70a1026)