{"record":{"id":"29bc63375ccf13ee","repo":"JuliusBrussee/caveman","slug":"artifactid-is-required","errorCode":null,"errorMessage":"artifactId is required","messagePattern":"artifactId is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/index.ts","lineNumber":2144,"sourceCode":"  try {\n    if (typeof response.text === \"function\") {\n      decoded = JSON.parse(await response.text());\n    } else if (typeof response.json === \"function\") {\n      decoded = await response.json();\n    } else {\n      throw new Error(\"missing response decoder\");\n    }\n  } catch {\n    throw new CaveRequestError(response.status, path, \"cave response was not valid JSON\");\n  }\n  if (decoded === null || typeof decoded !== \"object\" || Array.isArray(decoded)) {\n    throw new CaveRequestError(response.status, path, \"cave response must be a JSON object\");\n  }\n  return decoded as Record<string, unknown>;\n}\n\nasync function artifactGet(cave: Cave, artifactId: string, workflow: string, trace: TraceContext): Promise<unknown> {\n  if (typeof artifactId !== \"string\" || artifactId.trim() === \"\") throw new Error(\"artifactId is required\");\n  const path = `/sdk/v1/artifacts/${encodeURIComponent(artifactId)}`;\n  const response = await caveFetch(cave, `${cave.options.baseURL}${path}`, {\n    method: \"GET\",\n    headers: headers(cave, workflow, undefined, undefined, trace)\n  });\n  if (!response.ok) {\n    discardResponse(response);\n    throw new CaveRequestError(response.status, path, `cave request failed (${response.status})`);\n  }\n  try {\n    return JSON.parse(await response.text());\n  } catch {\n    throw new CaveRequestError(response.status, path, \"cave artifact was not valid JSON\");\n  }\n}\n\nfunction headers(cave: Cave, workflow: string, upstreamKey?: string, hint?: Record<string, unknown>, trace?: TraceContext) {\n  return {","sourceCodeStart":2126,"sourceCodeEnd":2162,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/typescript/src/index.ts#L2126-L2162","documentation":"Synchronous input validation at the top of artifactGet: the artifactId argument must be a non-empty, non-whitespace string, otherwise a plain Error(\"artifactId is required\") is thrown before any network call is made. This is a fail-fast guard so callers get an immediate programming-error signal instead of a confusing 404 from the server.","triggerScenarios":"Calling an SDK method that internally fetches an artifact while passing undefined, null, an empty string \"\", or a whitespace-only string as the artifact identifier — typically when the ID comes from an upstream API response field or a variable that was never populated.","commonSituations":"Destructuring an artifact reference from a prior response that omitted the id field; template strings like `art-${n}` with n undefined; TypeScript types bypassed via any/as-casts; optional IDs from config files left blank.","solutions":["Validate the artifact ID before calling: if (typeof id !== 'string' || id.trim() === '') throw new Error(...)","Trace where the ID originates and fix the upstream producer that left it empty","Use TypeScript strict mode / non-null assertions correctly so optional ID fields cannot flow through unchecked","Add a unit test covering the empty-ID path of the wrapper function you call"],"exampleFix":"// before\nconst artifact = await sdk.getArtifact(record.artifactId); // record.artifactId is ''\n// after\nif (typeof record.artifactId !== 'string' || record.artifactId.trim() === '') {\n  throw new Error(`missing artifactId on record ${record.id}`);\n}\nconst artifact = await sdk.getArtifact(record.artifactId);","handlingStrategy":"validation","validationCode":"function requireArtifactId(id: unknown): string { if (typeof id !== 'string' || id.trim() === '') throw new Error('artifactId is required'); return id; }","typeGuard":"const hasArtifactId = (v: unknown): v is { artifactId: string } => typeof v === 'object' && v !== null && typeof (v as any).artifactId === 'string' && (v as any).artifactId.trim() !== '';","tryCatchPattern":"try { const a = await sdk.getArtifact(id, wf); } catch (e) { if (e instanceof Error && e.message === 'artifactId is required') throw new Error(`upstream record missing artifactId: ${JSON.stringify(record).slice(0, 200)}`); throw e; }","preventionTips":["Validate IDs at the boundary where records are produced/consumed","Enable TypeScript strict null checks and avoid any-typed ID flows","Fail fast with context (record id) when an upstream field is missing","Unit-test the empty/whitespace ID path of your wrapper functions"],"tags":["validation","typescript","argument"],"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"}