{"record":{"id":"5a5977904e2c3dbd","repo":"JuliusBrussee/caveman","slug":"apikey-baseurl-and-agent-are-required","errorCode":null,"errorMessage":"apiKey, baseURL, and agent are required","messagePattern":"apiKey, baseURL, and agent are required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/index.ts","lineNumber":354,"sourceCode":"  if (value.trim() !== value) throw new Error(`${name} must not contain surrounding whitespace`);\n  let parsed: URL;\n  try {\n    parsed = new URL(value);\n  } catch {\n    throw new Error(`${name} must be an absolute http(s) URL`);\n  }\n  if ((parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") || !parsed.hostname || parsed.username || parsed.password) {\n    throw new Error(`${name} must be an absolute http(s) URL without credentials`);\n  }\n  if (parsed.search || parsed.hash) throw new Error(`${name} must not contain a query or fragment`);\n  return value.replace(/\\/+$/, \"\");\n}\n\nexport class Cave {\n  readonly options: CaveOptions;\n\n  constructor(options: CaveOptions) {\n    if (!options.apiKey || !options.baseURL || !options.agent) throw new Error(\"apiKey, baseURL, and agent are required\");\n    if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs <= 0)) {\n      throw new Error(\"timeoutMs must be a positive integer\");\n    }\n    // CAVE_WORKFLOW lets a wrapper (`cave wrap --workflow x`) label every request\n    // from an SDK app without a code change. An explicit option always wins.\n    // globalThis lookup keeps the SDK browser-neutral (no node type dependency).\n    // The env value is normalized to the gateway's label rule (lowercase\n    // [a-z0-9_-], max 96) — an invalid ambient value is ignored rather than\n    // 400-ing every request. Mirrors caveman_cloud (Python).\n    const envRaw = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.[\"CAVE_WORKFLOW\"]?.toLowerCase();\n    const envWorkflow = envRaw && /^[a-z0-9_-]{1,96}$/.test(envRaw) ? envRaw : undefined;\n    const normalized: CaveOptions = {\n      ...options,\n      baseURL: normalizedServiceURL(options.baseURL, \"baseURL\"),\n      ...(options.controlURL === undefined\n        ? {}\n        : { controlURL: normalizedServiceURL(options.controlURL, \"controlURL\") }),\n    };","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/typescript/src/index.ts#L336-L372","documentation":"The Cave constructor validates its options up front and requires three fields: apiKey, baseURL, and agent. If any of them is falsy (missing, undefined, empty string), construction fails immediately rather than failing later on the first request. It enforces that every client instance is fully configured at creation time.","triggerScenarios":"new Cave({ apiKey: undefined, baseURL, agent }) — e.g. the API key env var is unset, baseURL omitted, or agent name forgotten; or an empty string passed for any of the three.","commonSituations":"Env var like CAVE_API_KEY not set in the deployment environment; options object built conditionally and one field dropped; TypeScript types bypassed with `as CaveOptions` so a missing field compiles; refactor renamed a field but old callers still pass the old name.","solutions":["Set the missing field(s) — verify apiKey, baseURL, and agent are all non-empty strings before constructing.","Check that the environment variable sourcing the API key is defined in the runtime environment.","Add a constructor-argument check or schema validation at app startup so misconfiguration is caught early."],"exampleFix":"// before\nconst cave = new Cave({ baseURL, agent: \"checkout\" });\n\n// after\nconst cave = new Cave({ apiKey: process.env.CAVE_API_KEY!, baseURL, agent: \"checkout\" });","handlingStrategy":"validation","validationCode":"function assertCaveOptions(o: Partial<CaveOptions> & Record<string, unknown>): asserts o is CaveOptions {\n  for (const k of [\"apiKey\", \"baseURL\", \"agent\"] as const)\n    if (!o[k] || typeof o[k] !== \"string\") throw new Error(`${k} is required and must be a non-empty string`);\n}","typeGuard":"const hasRequiredCaveOptions = (o: Partial<CaveOptions>): o is CaveOptions & { apiKey: string; baseURL: string; agent: string } =>\n  typeof o.apiKey === \"string\" && o.apiKey.length > 0 &&\n  typeof o.baseURL === \"string\" && o.baseURL.length > 0 &&\n  typeof o.agent === \"string\" && o.agent.length > 0;","tryCatchPattern":"try {\n  const cave = new Cave(options);\n} catch (e) {\n  if (e instanceof Error && e.message === \"apiKey, baseURL, and agent are required\") {\n    // fail fast with a targeted message naming which option is missing\n    const missing = [\"apiKey\", \"baseURL\", \"agent\"].filter(k => !(options as any)[k]);\n    throw new Error(`Cave misconfigured: missing ${missing.join(\", \")}`);\n  } else throw e;\n}","preventionTips":["Construct the Cave client once at startup with a validated options object.","Check required env vars (API key, base URL) at boot and fail with clear messages.","Avoid `as CaveOptions` casts that hide missing fields from the type checker."],"tags":["validation","constructor","configuration","missing-api-key"],"backgroundTag":"missing-required-argument","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"}