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

  1. Read the status: 401/403 → check CAVE_TOKEN validity and project access; 404 → verify CAVE_PROJECT_ID and CAVE_CONTROL_URL; 5xx → retry later.
  2. Confirm the token has permission for this project in the control plane UI.
  3. Curl the same endpoint manually with `Authorization: Bearer $CAVE_TOKEN` to see the response body, which the CLI discards.
  4. 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

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


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)