JuliusBrussee/caveman · error · Error

registration failed: HTTP ${response.status}

Error message

registration failed: HTTP ${response.status}

What it means

The POST to {controlURL}/api/v1/projects/{projectID}/agent-builds completed but response.ok was false, so the CLI surfaces the HTTP status verbatim. The request already passed env validation and the stale-lock gate; this is the server rejecting the registration (auth, project id, payload, or server-side error). The fetch has a 10s AbortSignal timeout, which surfaces as an AbortError, not this message.

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 27d5a3981a)

Solutions

  1. Decode the status: 401/403 → rotate/refresh CAVE_TOKEN; 404 → verify CAVE_PROJECT_ID and CAVE_CONTROL_URL; 409 → build already registered, nothing to do; 5xx → retry or check the control plane.
  2. Re-check that CAVE_CONTROL_URL is the correct environment base URL with no /api suffix (the CLI appends the path).
  3. If 400 persists, compare CLI and control-plane versions — the payload contract may have moved; upgrade the caveman agent package.

Example fix

# before
export CAVE_CONTROL_URL=https://cave.internal   # wrong env (404)
caveman-agent register

# after
export CAVE_CONTROL_URL=https://cave.example.com
caveman-agent register
Defensive patterns

Strategy: retry

Validate before calling

function registrationTargetPlausible(): { ok: boolean; reason?: string } {
  const url = process.env.CAVE_CONTROL_URL;
  if (!url?.startsWith("https://")) return { ok: false, reason: "CAVE_CONTROL_URL must be an https base URL" };
  if (!/^[A-Za-z0-9_-]+$/.test(process.env.CAVE_PROJECT_ID ?? "")) return { ok: false, reason: "CAVE_PROJECT_ID malformed" };
  return { ok: true };
}

Try / catch

const RETRYABLE = new Set([408, 429, 502, 503, 504]);
let attempt = 0;
while (true) {
  try {
    return await register(args);
  } catch (error) {
    const status = Number((error as Error).message.match(/HTTP (\d+)/)?.[1]);
    if (RETRYABLE.has(status) && ++attempt < 4) {
      await sleep(2 ** attempt * 500); // exponential backoff on 429/5xx only
      continue;
    }
    if (status === 409) return; // already registered — treat as success
    throw error; // 401/403/404 are config defects, do not retry
  }
}

Prevention

When it happens

Trigger: 401/403 from an invalid or expired CAVE_TOKEN; 404 from a wrong CAVE_PROJECT_ID or wrong CAVE_CONTROL_URL base path; 400/409 from an already-registered build_sha256 or schema mismatch after CLI/server version drift; 5xx from a control-plane outage.

Common situations: Expired registration token in CI; pointing CAVE_CONTROL_URL at staging while the project lives in prod (or vice versa); re-registering the same build; server API evolved beyond the CLI's payload shape.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/cd534fd359437340. Report an issue: GitHub.