JuliusBrussee/caveman · error

apiKey, baseURL, and agent are required

Error message

apiKey, baseURL, and agent are required

What it means

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.

Solutions

  1. Set the missing field(s) — verify apiKey, baseURL, and agent are all non-empty strings before constructing.
  2. Check that the environment variable sourcing the API key is defined in the runtime environment.
  3. Add a constructor-argument check or schema validation at app startup so misconfiguration is caught early.

Example fix

// before
const cave = new Cave({ baseURL, agent: "checkout" });

// after
const cave = new Cave({ apiKey: process.env.CAVE_API_KEY!, baseURL, agent: "checkout" });
Defensive patterns

Strategy: validation

Validate before calling

function assertCaveOptions(o: Partial<CaveOptions> & Record<string, unknown>): asserts o is CaveOptions {
  for (const k of ["apiKey", "baseURL", "agent"] as const)
    if (!o[k] || typeof o[k] !== "string") throw new Error(`${k} is required and must be a non-empty string`);
}

Type guard

const hasRequiredCaveOptions = (o: Partial<CaveOptions>): o is CaveOptions & { apiKey: string; baseURL: string; agent: string } =>
  typeof o.apiKey === "string" && o.apiKey.length > 0 &&
  typeof o.baseURL === "string" && o.baseURL.length > 0 &&
  typeof o.agent === "string" && o.agent.length > 0;

Try / catch

try {
  const cave = new Cave(options);
} catch (e) {
  if (e instanceof Error && e.message === "apiKey, baseURL, and agent are required") {
    // fail fast with a targeted message naming which option is missing
    const missing = ["apiKey", "baseURL", "agent"].filter(k => !(options as any)[k]);
    throw new Error(`Cave misconfigured: missing ${missing.join(", ")}`);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/5a5977904e2c3dbd. Report an issue: GitHub.

Appendix: source

Thrown at packages/sdk/typescript/src/index.ts:354

  if (value.trim() !== value) throw new Error(`${name} must not contain surrounding whitespace`);
  let parsed: URL;
  try {
    parsed = new URL(value);
  } catch {
    throw new Error(`${name} must be an absolute http(s) URL`);
  }
  if ((parsed.protocol !== "http:" && parsed.protocol !== "https:") || !parsed.hostname || parsed.username || parsed.password) {
    throw new Error(`${name} must be an absolute http(s) URL without credentials`);
  }
  if (parsed.search || parsed.hash) throw new Error(`${name} must not contain a query or fragment`);
  return value.replace(/\/+$/, "");
}

export class Cave {
  readonly options: CaveOptions;

  constructor(options: CaveOptions) {
    if (!options.apiKey || !options.baseURL || !options.agent) throw new Error("apiKey, baseURL, and agent are required");
    if (options.timeoutMs !== undefined && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs <= 0)) {
      throw new Error("timeoutMs must be a positive integer");
    }
    // CAVE_WORKFLOW lets a wrapper (`cave wrap --workflow x`) label every request
    // from an SDK app without a code change. An explicit option always wins.
    // globalThis lookup keeps the SDK browser-neutral (no node type dependency).
    // The env value is normalized to the gateway's label rule (lowercase
    // [a-z0-9_-], max 96) — an invalid ambient value is ignored rather than
    // 400-ing every request. Mirrors caveman_cloud (Python).
    const envRaw = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.["CAVE_WORKFLOW"]?.toLowerCase();
    const envWorkflow = envRaw && /^[a-z0-9_-]{1,96}$/.test(envRaw) ? envRaw : undefined;
    const normalized: CaveOptions = {
      ...options,
      baseURL: normalizedServiceURL(options.baseURL, "baseURL"),
      ...(options.controlURL === undefined
        ? {}
        : { controlURL: normalizedServiceURL(options.controlURL, "controlURL") }),
    };

View on GitHub (pinned to 3ee70a1026)