JuliusBrussee/caveman · error
registration failed: HTTP
Error message
registration failed: HTTP ${response.status} What it means
After register posts the lock to `${CAVE_CONTROL_URL}/api/v1/projects/{id}/agent-builds` (with a 10s AbortSignal timeout), any non-2xx HTTP status is converted into this generic error containing the status code. The control plane rejected the registration request for a reason the CLI does not interpret.
Solutions
- Read the status: 401/403 → check CAVE_TOKEN validity and project access; 404 → verify CAVE_PROJECT_ID and CAVE_CONTROL_URL; 5xx → retry later.
- Confirm the token has permission for this project in the control plane UI.
- Curl the same endpoint manually with `Authorization: Bearer $CAVE_TOKEN` to see the response body, which the CLI discards.
- Check control-plane health/status page if 5xx persists.
Example fix
// before caveman register # registration failed: HTTP 401 // after export CAVE_TOKEN=<freshly-issued-token> caveman register
Defensive patterns
Strategy: retry
Validate before calling
const res = await fetch(`${process.env.CAVE_CONTROL_URL}/api/v1/health`, {
headers: { authorization: `Bearer ${process.env.CAVE_TOKEN}` },
});
if (res.status === 401 || res.status === 403) throw new Error("CAVE_TOKEN invalid for this project"); Try / catch
try {
await register();
} catch (err) {
const m = /registration failed: HTTP (\d+)/.exec(String(err?.message));
if (m) {
const status = Number(m[1]);
if (status >= 500) await register(); // retry transient
else console.error(`Control plane rejected: ${status}; check token/project id`);
} else throw err;
} Prevention
- Pre-flight the control plane with a cheap authenticated request before registering.
- Rotate tokens on a schedule and update CI secrets promptly.
- Verify CAVE_PROJECT_ID exists in the target environment (staging vs production).
- Add status-specific handling rather than treating all failures alike.
When it happens
Trigger: The fetch to the agent-builds endpoint returns response.ok === false — e.g. 401 for an invalid/expired CAVE_TOKEN, 403 for a token lacking access to CAVE_PROJECT_ID, 404 for a wrong project id or control URL path, 5xx for control-plane downtime.
Common situations: Rotated or revoked tokens not updated in CI; project id from a different environment; control-plane URL pointing at staging while the token is for production; control plane temporarily down.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- cave_request_failed
- cave request failed ( )
- tool search failed with HTTP
- awscreds: sts assume role with web identity failed
- binary download failed: HTTP
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/cd534fd359437340.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/cli.ts:502
agent_slug: lock.agent_id,
build_sha256: lock.build_sha256,
plan_sha256: lock.plan_sha256,
source_sha256: lock.source_sha256,
eval_suite_sha256: lock.eval_suite_sha256,
catalog_sha256: lock.catalog_sha256,
transform_registry_sha256: lock.runtime.transform_registry_sha256,
harness: lock.harness.id,
adapter_version: lock.harness.adapter_version,
upstream_version: lock.harness.upstream_version,
runtime_version: lock.runtime.caveman_version,
evidence_status: lock.evidence.status,
evidence_basis: lock.evidence.basis,
lock,
}),
signal: AbortSignal.timeout(10_000),
});
if (!response.ok) {
throw new Error(`registration failed: HTTP ${response.status}`);
}
const registered = await response.json() as { id?: unknown; build_sha256?: unknown };
process.stdout.write([
`registered build: ${String(registered.build_sha256 ?? lock.build_sha256)}`,
`registration id: ${String(registered.id ?? "unavailable")}`,
"registration does not activate plan or create a savings claim",
"provider savings: not claimed",
"",
].join("\n"));
}
async function dev(args: string[]): Promise<void> {
const root = process.cwd();
const entry = args[0] ?? "src/agent.ts";
const prompt = args.slice(1).join(" ") || "Reply with one short greeting.";
const conversation = createConversation();
const sessionId = conversation.sessionId;
const interactive = args.length <= 1 && process.stdin.isTTY && process.stdout.isTTY;View on GitHub (pinned to 3ee70a1026)