{"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":"validation","errorClass":null,"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/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/cli.ts#L451-L487","documentation":"The `register` CLI command uploads a build lock to a CAVE control-plane so a build can be registered against a project. Before doing anything it reads three environment variables: CAVE_CONTROL_URL (base URL of the control plane, trailing slashes stripped), CAVE_TOKEN (or CAVE_API_TOKEN), and CAVE_PROJECT_ID. If any of the three is unset or empty, it throws this error instead of making a doomed network call.","triggerScenarios":"Running `caveman register` (via main) when CAVE_CONTROL_URL, CAVE_TOKEN/CAVE_API_TOKEN, or CAVE_PROJECT_ID is unset or an empty string in the process environment.","commonSituations":"Fresh CI job or new shell that sourced no cave.env file; CI secrets configured under the wrong names or mapped into the job environment incompletely; setting CAVE_TOKEN='' which fails the truthiness check; typos like CAVE_CONTOL_URL; forgetting per-project CAVE_PROJECT_ID when switching projects.","solutions":["Export all three variables before running: CAVE_CONTROL_URL, CAVE_TOKEN (or CAVE_API_TOKEN), CAVE_PROJECT_ID.","Source your env file (e.g. `set -a; source cave.env; set +a;`) or use `env $(cat cave.env | xargs)` in CI.","Check for typos/empty values: `printenv CAVE_CONTROL_URL CAVE_TOKEN CAVE_PROJECT_ID`.","In CI, verify the secrets are actually mapped into the job's environment, not just defined as repo secrets."],"exampleFix":"// before\nnpx caveman register\n// after\nexport CAVE_CONTROL_URL=https://control.example.com\nexport CAVE_TOKEN=$CAVE_API_TOKEN\nexport CAVE_PROJECT_ID=proj_123\nnpx caveman register","handlingStrategy":"validation","validationCode":"for (const v of [\"CAVE_CONTROL_URL\",\"CAVE_TOKEN\",\"CAVE_PROJECT_ID\"]) {\n  if (!process.env[v]) throw new Error(`${v} is not set; cannot run caveman register`);\n}","typeGuard":"const hasCaveEnv = (e: NodeJS.ProcessEnv): e is NodeJS.ProcessEnv &\n  Record<\"CAVE_CONTROL_URL\"|\"CAVE_TOKEN\"|\"CAVE_PROJECT_ID\", string> =>\n  Boolean(e.CAVE_CONTROL_URL && (e.CAVE_TOKEN ?? e.CAVE_API_TOKEN) && e.CAVE_PROJECT_ID);","tryCatchPattern":"try {\n  await run([\"caveman\", \"register\"]);\n} catch (err) {\n  if (String(err?.message).includes(\"register requires CAVE_\")) {\n    console.error(\"Set CAVE_CONTROL_URL, CAVE_TOKEN and CAVE_PROJECT_ID first\");\n  }\n  throw err;\n}","preventionTips":["Commit a cave.env.example listing all required vars and source it in CI and locally.","Fail fast in scripts: check env vars before invoking the CLI.","Keep names consistent; prefer CAVE_TOKEN and document the CAVE_API_TOKEN alias.","In CI, assert secrets are injected via a preflight step."],"tags":["cli","env","configuration"],"backgroundTag":"missing-env-var","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}