JuliusBrussee/caveman · warning · Error

cave_harness_adapter_version_invalid

cave_harness_adapter_version_invalid

Error message

cave_harness_adapter_version_invalid

What it means

SanitizeAttributes rejects any single attribute key longer than MaxAttributeKeyBytes (256 bytes). Long keys bloat span payloads and are frequently malformed or injection-like, so the sanitizer fails closed instead of hashing or truncating them. The limit is checked on the sorted allowlisted keys before values are copied.

Source

Thrown at packages/agent/src/adapters.ts:90

  upstreamVersion: string;
  bundleSHA256: string;
  dependencyLockSHA256: string;
}

export interface HarnessAdapterManifest extends HarnessAdapterIdentity {
  schemaVersion: 1;
  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));

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Bound the variable part of the key: hash or truncate any identifier embedded in the key and move the full value into the attribute value.
  2. Validate key length at the call site before adding the attribute (len(key) <= 256).
  3. Move unbounded labels from the key into the value or a dedicated log field.

Example fix

// before
key := "request.header." + strings.ToLower(rawHeaderName) // rawHeaderName unbounded
attrs[key] = "present"

// after
name := strings.ToLower(rawHeaderName)
if len(name) > telemetry.MaxAttributeKeyBytes {
    name = "long-header-" + fmt.Sprintf("%x", sha256.Sum256([]byte(name)))
}
attrs["request.header."+name] = "present"
Defensive patterns

Strategy: validation

Validate before calling

func sanitizeKey(k string) (string, bool) {
    if len(k) > telemetry.MaxAttributeKeyBytes {
        return "", false
    }
    return k, true
}

for k, v := range raw {
    if k2, ok := sanitizeKey(k); ok {
        attrs[k2] = v
    }
}

Type guard

func validAttrKey(k string) bool {
    return len(k) <= telemetry.MaxAttributeKeyBytes
}

Prevention

When it happens

Trigger: Calling SanitizeAttributes with at least one allowlisted key whose string length exceeds 256 bytes (e.g. a generated key like "feature.flag." + 300-char identifier, or a templated key built from a URL path).

Common situations: Composing attribute keys from unbounded user input (paths, ids, serialized labels); copying OpenTelemetry semantic-convention keys with long prefixes; key templates that append tenant or shard identifiers.

Related errors


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