{"record":{"id":"e9b6cedbd2c4f1d7","repo":"JuliusBrussee/caveman","slug":"register-requires-cave-control-url-cave-token-an","errorCode":null,"errorMessage":"register requires CAVE_CONTROL_URL, CAVE_TOKEN, and CAVE_PROJECT_ID","messagePattern":"register requires CAVE_CONTROL_URL, CAVE_TOKEN, and CAVE_PROJECT_ID","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/cli.ts","lineNumber":469,"sourceCode":"  if (provider === \"openai\") {\n    return { name: \"OPENAI_API_KEY\", available: Boolean(process.env.OPENAI_API_KEY) };\n  }\n  if (provider === \"google\" || provider === \"gemini\") {\n    return {\n      name: \"GEMINI_API_KEY or GOOGLE_API_KEY\",\n      available: Boolean(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY),\n    };\n  }\n  return { name: `credential for ${provider || \"unknown provider\"}`, available: false };\n}\n\nasync function register(_args: string[]): Promise<void> {\n  const root = process.cwd();\n  const controlURL = process.env.CAVE_CONTROL_URL?.replace(/\\/+$/, \"\");\n  const token = process.env.CAVE_TOKEN ?? process.env.CAVE_API_TOKEN;\n  const projectID = process.env.CAVE_PROJECT_ID;\n  if (!controlURL || !token || !projectID) {\n    throw new Error(\"register requires CAVE_CONTROL_URL, CAVE_TOKEN, and CAVE_PROJECT_ID\");\n  }\n  const lock = await readLock(root);\n  const loaded = await loadBuildInputs(root, \"caveman.config.ts\");\n  const checked = await validLockIdentity(root, loaded.config.entry);\n  if (!checked || checked.build_sha256 !== lock.build_sha256) {\n    throw new Error(\"cave_stale_lock:registration\");\n  }\n  const response = await fetch(`${controlURL}/api/v1/projects/${encodeURIComponent(projectID)}/agent-builds`, {\n    method: \"POST\",\n    headers: {\n      authorization: `Bearer ${token}`,\n      \"content-type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      agent_slug: lock.agent_id,\n      build_sha256: lock.build_sha256,\n      plan_sha256: lock.plan_sha256,\n      source_sha256: lock.source_sha256,","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/cli.ts#L451-L487","documentation":"The register subcommand uploads a validated build to a Cave control plane and requires three env vars: CAVE_CONTROL_URL (the server base URL), CAVE_TOKEN or CAVE_API_TOKEN (bearer credential), and CAVE_PROJECT_ID (target project). If any is missing or CAVE_CONTROL_URL is empty/whitespace-only, registration refuses to start — it will not guess an endpoint or register anonymously.","triggerScenarios":"Running `caveman-agent register` with one or more of CAVE_CONTROL_URL, CAVE_TOKEN/CAVE_API_TOKEN, CAVE_PROJECT_ID unset or set to an empty string. Note CAVE_CONTROL_URL has trailing slashes stripped, so \"/\"-only values also collapse to falsy.","commonSituations":"CI secret not mapped into the job env; local shell missing the exported vars (defined in a different terminal or direnv not loaded); token present under a differently-named var (CAVE_API_KEY instead of the accepted names).","solutions":["Export all three: CAVE_CONTROL_URL=https://cave.example.com CAVE_TOKEN=… CAVE_PROJECT_ID=… caveman-agent register.","If your secret is named CAVE_API_TOKEN, that alias is accepted — but CAVE_API_KEY is not; rename or additionally set CAVE_TOKEN.","In CI, verify the three secrets are mapped as env vars on the register step (print a boolean presence check, never the values)."],"exampleFix":"# before\nexport CAVE_CONTROL_URL=https://cave.example.com\ncaveman-agent register # throws: token/project missing\n\n# after\nexport CAVE_CONTROL_URL=https://cave.example.com\nexport CAVE_TOKEN=$CAVE_REGISTER_TOKEN\nexport CAVE_PROJECT_ID=acme-core\ncaveman-agent register","handlingStrategy":"validation","validationCode":"function registerEnvReady(): boolean {\n  const url = process.env.CAVE_CONTROL_URL?.replace(/\\/+$/, \"\");\n  const token = process.env.CAVE_TOKEN ?? process.env.CAVE_API_TOKEN;\n  return Boolean(url && token && process.env.CAVE_PROJECT_ID);\n}\nif (!registerEnvReady()) {\n  throw new Error(\"set CAVE_CONTROL_URL, CAVE_TOKEN (or CAVE_API_TOKEN), and CAVE_PROJECT_ID\");\n}","typeGuard":"interface RegisterEnv { controlUrl: string; token: string; projectId: string }\nfunction parseRegisterEnv(env: NodeJS.ProcessEnv): RegisterEnv | null {\n  const controlUrl = env.CAVE_CONTROL_URL?.replace(/\\/+$/, \"\");\n  const token = env.CAVE_TOKEN ?? env.CAVE_API_TOKEN;\n  const projectId = env.CAVE_PROJECT_ID;\n  return controlUrl && token && projectId ? { controlUrl, token, projectId } : null;\n}","tryCatchPattern":null,"preventionTips":["Put all three vars (token may be CAVE_TOKEN or CAVE_API_TOKEN — no other alias) in the register step's env in CI.","Fail fast with your own presence check in wrapper scripts so the error names what's missing.","Never print the token value when debugging env — check presence only."],"tags":["cli","environment","registration","env"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}