JuliusBrussee/caveman · error

artifactId is required

Error message

artifactId is required

What it means

Thrown by artifactGet() in the TypeScript SDK when the artifactId argument is not a string or is empty/whitespace-only. It is a local client-side guard fired before any network request to GET /sdk/v1/artifacts/{id}. The SDK refuses to build a URL from an unusable identifier.

Source

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

  try {
    if (typeof response.text === "function") {
      decoded = JSON.parse(await response.text());
    } else if (typeof response.json === "function") {
      decoded = await response.json();
    } else {
      throw new Error("missing response decoder");
    }
  } catch {
    throw new CaveRequestError(response.status, path, "cave response was not valid JSON");
  }
  if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded)) {
    throw new CaveRequestError(response.status, path, "cave response must be a JSON object");
  }
  return decoded as Record<string, unknown>;
}

async function artifactGet(cave: Cave, artifactId: string, workflow: string, trace: TraceContext): Promise<unknown> {
  if (typeof artifactId !== "string" || artifactId.trim() === "") throw new Error("artifactId is required");
  const path = `/sdk/v1/artifacts/${encodeURIComponent(artifactId)}`;
  const response = await caveFetch(cave, `${cave.options.baseURL}${path}`, {
    method: "GET",
    headers: headers(cave, workflow, undefined, undefined, trace)
  });
  if (!response.ok) throw new CaveRequestError(response.status, path, `cave request failed (${response.status})`);
  try {
    return JSON.parse(await response.text());
  } catch {
    throw new CaveRequestError(response.status, path, "cave artifact was not valid JSON");
  }
}

function headers(cave: Cave, workflow: string, upstreamKey?: string, hint?: Record<string, unknown>, trace?: TraceContext) {
  return {
    "content-type": "application/json",
    authorization: `Bearer ${cave.options.apiKey}`,
    "x-cave-agent": cave.options.agent,

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass a non-empty string artifact id exactly as returned by artifacts.page() / the gateway
  2. If the id comes from untrusted data, trim and validate it before calling get()
  3. Type the id as string at call sites so the compiler rejects undefined at build time

Example fix

// before
const art = await trace.artifacts.get(resp.artifact_id); // undefined if field absent

// after
const id = resp.artifact_id;
if (typeof id !== "string" || id.trim() === "") throw new Error("no artifact id in response");
const art = await trace.artifacts.get(id);
Defensive patterns

Strategy: validation

Validate before calling

function isArtifactId(v) {
  return typeof v === "string" && v.trim() !== "";
}
if (!isArtifactId(id)) throw new TypeError("artifact id missing");

Type guard

function isArtifactId(v) {
  return typeof v === "string" && v.trim().length > 0;
}

Prevention

When it happens

Trigger: Calling CaveTrace.artifacts.get(id) (which delegates to artifactGet) with undefined, null, a number, an array, or an empty/whitespace string — typically the result of destructuring an id from an object that never had one, or using a checkpoint source_ref instead of an artifact id.

Common situations: Reading an id from an API response that renamed the field; passing a handle from a failed artifacts.page() call; default parameters evaluating to undefined; ids from JSON.parse typed loosely as any.

Related errors


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