JuliusBrussee/caveman · warning · Error
cave_harness_artifact_digest_invalid
cave_harness_artifact_digest_invalid
Error message
cave_harness_artifact_digest_invalid
What it means
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.
Source
Thrown at packages/agent/src/adapters.ts:97
harness: HarnessID;
wireContract: Readonly<Record<string, unknown>>;
}
export function createHarnessAdapter(
id: HarnessID,
identity: HarnessAdapterIdentity,
wireContract: Readonly<Record<string, unknown>>,
invoke: HarnessInvoke,
): HarnessAdapter {
if (!/^[0-9A-Za-z][0-9A-Za-z._-]{0,63}$/.test(identity.adapterVersion)) {
throw new Error("cave_harness_adapter_version_invalid");
}
if (!/^[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$/.test(identity.upstreamVersion)) {
throw new Error("cave_harness_upstream_version_invalid");
}
if (!/^[0-9a-f]{64}$/.test(identity.bundleSHA256) ||
!/^[0-9a-f]{64}$/.test(identity.dependencyLockSHA256)) {
throw new Error("cave_harness_artifact_digest_invalid");
}
const manifest = deepFreeze({
schemaVersion: 1 as const,
harness: id,
adapterVersion: identity.adapterVersion,
upstreamVersion: identity.upstreamVersion,
bundleSHA256: identity.bundleSHA256,
dependencyLockSHA256: identity.dependencyLockSHA256,
wireContract: canonicalRecord(wireContract),
});
const contractSHA256 = sha256(stableStringify(manifest));
return Object.freeze({
id,
version: identity.adapterVersion,
manifest,
contractSHA256,
async run(request: HarnessRequest): Promise<HarnessResult> {
const frozenRequest = snapshotRequest(request);View on GitHub (pinned to 27d5a3981a)
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.
Example fix
// before
for _, kv := range collected {
attrs[kv.key] = kv.value // no aggregate budget
}
// after
var total int
for _, kv := range collected {
if total+len(kv.key)+len(kv.value) > telemetry.MaxAttributesBytes {
break // stop before the aggregate cap
}
attrs[kv.key] = kv.value
total += len(kv.key) + len(kv.value)
} Defensive patterns
Strategy: validation
Validate before calling
func withinAttrBudget(attrs map[string]string) bool {
total := 0
for k, v := range attrs {
if !telemetry.AttributeAllowed(k) {
continue
}
total += len(k) + len(v)
if total > telemetry.MaxAttributesBytes {
return false
}
}
return true
}
if !withinAttrBudget(attrs) {
attrs = pruneToBudget(attrs, telemetry.MaxAttributesBytes)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cave_harness_upstream_version_invalid
- option not found
- cave_harness_adapter_version_invalid
- cave_sandbox_input_limit
- hook payload too large
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/e52c3f1e9cecb001.
Report an issue: GitHub.