{"record":{"id":"e52c3f1e9cecb001","repo":"JuliusBrussee/caveman","slug":"cave-harness-artifact-digest-invalid","errorCode":"cave_harness_artifact_digest_invalid","errorMessage":"cave_harness_artifact_digest_invalid","messagePattern":"cave_harness_artifact_digest_invalid","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/agent/src/adapters.ts","lineNumber":97,"sourceCode":"  harness: HarnessID;\n  wireContract: Readonly<Record<string, unknown>>;\n}\n\nexport function createHarnessAdapter(\n  id: HarnessID,\n  identity: HarnessAdapterIdentity,\n  wireContract: Readonly<Record<string, unknown>>,\n  invoke: HarnessInvoke,\n): HarnessAdapter {\n  if (!/^[0-9A-Za-z][0-9A-Za-z._-]{0,63}$/.test(identity.adapterVersion)) {\n    throw new Error(\"cave_harness_adapter_version_invalid\");\n  }\n  if (!/^[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$/.test(identity.upstreamVersion)) {\n    throw new Error(\"cave_harness_upstream_version_invalid\");\n  }\n  if (!/^[0-9a-f]{64}$/.test(identity.bundleSHA256) ||\n      !/^[0-9a-f]{64}$/.test(identity.dependencyLockSHA256)) {\n    throw new Error(\"cave_harness_artifact_digest_invalid\");\n  }\n  const manifest = deepFreeze({\n    schemaVersion: 1 as const,\n    harness: id,\n    adapterVersion: identity.adapterVersion,\n    upstreamVersion: identity.upstreamVersion,\n    bundleSHA256: identity.bundleSHA256,\n    dependencyLockSHA256: identity.dependencyLockSHA256,\n    wireContract: canonicalRecord(wireContract),\n  });\n  const contractSHA256 = sha256(stableStringify(manifest));\n  return Object.freeze({\n    id,\n    version: identity.adapterVersion,\n    manifest,\n    contractSHA256,\n    async run(request: HarnessRequest): Promise<HarnessResult> {\n      const frozenRequest = snapshotRequest(request);","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/adapters.ts#L79-L115","documentation":"SanitizeAttributes enforces a cumulative budget of MaxAttributesBytes (64 KiB) across all key+value bytes combined. Even when each individual key and value passes its own per-item limit, many attributes together can exceed the aggregate cap; the running total is accumulated in sorted order and the function fails closed past 64 KiB.","triggerScenarios":"Calling SanitizeAttributes with dozens-to-hundreds of allowlisted attributes whose combined key and value byte lengths exceed 65536 — e.g. 40 attributes each near the 4096-byte value cap, or hundreds of medium-sized labels.","commonSituations":"Forwarding large label sets (Kubernetes-style metadata) into spans; attaching many truncated-but-still-large values that each individually pass; gradually accruing attributes across middleware layers.","solutions":["Budget attributes by priority: compute total bytes as you build the map and stop before the cumulative size nears 64 KiB.","Reduce per-value truncation limits (e.g. 1024 bytes) so aggregate size stays well under the cap.","Move bulky, low-signal attributes to logs and keep only identifiers and counters on the span."],"exampleFix":"// before\nfor _, kv := range collected {\n    attrs[kv.key] = kv.value // no aggregate budget\n}\n\n// after\nvar total int\nfor _, kv := range collected {\n    if total+len(kv.key)+len(kv.value) > telemetry.MaxAttributesBytes {\n        break // stop before the aggregate cap\n    }\n    attrs[kv.key] = kv.value\n    total += len(kv.key) + len(kv.value)\n}","handlingStrategy":"validation","validationCode":"func withinAttrBudget(attrs map[string]string) bool {\n    total := 0\n    for k, v := range attrs {\n        if !telemetry.AttributeAllowed(k) {\n            continue\n        }\n        total += len(k) + len(v)\n        if total > telemetry.MaxAttributesBytes {\n            return false\n        }\n    }\n    return true\n}\n\nif !withinAttrBudget(attrs) {\n    attrs = pruneToBudget(attrs, telemetry.MaxAttributesBytes)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Track a running byte budget while accumulating attributes instead of checking after the fact.","Use a smaller internal per-value cap (e.g. 1 KiB) so realistic attribute counts never reach 64 KiB total.","Treat attributes as metadata, not storage; route bulky content to logs."],"tags":["telemetry","limits","payload-size"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}