JuliusBrussee/caveman · error · Error
cave_harness_artifact_digest_invalid
cave_harness_artifact_digest_invalid
Error message
cave_harness_artifact_digest_invalid
What it means
`createHarnessAdapter` requires both `identity.bundleSHA256` and `identity.dependencyLockSHA256` to match `^[0-9a-f]{64}$` — exactly 64 lowercase hexadecimal characters. Uppercase hex, a `sha256:` prefix, short git-style SHAs, or an empty value throw before the manifest is frozen. These digests anchor the adapter's artifacts into the manifest and its contractSHA256, so format violations are treated as a broken evidence chain.
Solutions
- Compute digests with `crypto.createHash('sha256').update(bytes).digest('hex')` — always 64 lowercase hex
- Normalize inputs: `.replace(/^sha256:/, '').toLowerCase()` before constructing the identity
- Never substitute a git short SHA or a truncated hash for the full file digest
Example fix
// before bundleSHA256: ciDigest /* "sha256:9F2AC3…" uppercase with prefix */ // after bundleSHA256: ciDigest.replace(/^sha256:/, '').toLowerCase()
Defensive patterns
Strategy: type-guard
Validate before calling
import { createHash } from 'node:crypto';
const bundleSHA256 = createHash('sha256').update(bundleBytes).digest('hex');
const dependencyLockSHA256 = createHash('sha256').update(lockBytes).digest('hex');
// digest('hex') is always exactly 64 lowercase hex chars — the contract shape Type guard
const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
function isSha256Hex(v: unknown): v is string {
return typeof v === 'string' && SHA256_HEX_RE.test(v);
}
function validIdentity(i: HarnessAdapterIdentity): boolean {
return isSha256Hex(i.bundleSHA256) && isSha256Hex(i.dependencyLockSHA256);
} Prevention
- Always produce digests with createHash('sha256').digest('hex') — never uppercase tool output
- Strip 'sha256:' prefixes and lowercase before constructing the identity
- Never substitute git short SHAs or truncated hashes for full content digests
When it happens
Trigger: Passing `git rev-parse --short HEAD` output (7-12 hex chars); a digest from a CI step that uppercases checksums; container-style `"sha256:abcd…"` digests; passing `""` because the dependency lock was never hashed.
Common situations: Wiring digests from release pipelines that prefix or uppercase; substituting a git commit SHA for a content digest; hashing the wrong bytes (format passes but later identity checks fail) or forgetting one digest entirely.
Related errors
- artifactId is required
- assemble requires model and sessionId
- cave artifact response missing artifact_id
- cave checkpoint response missing source_ref or messages
- cave_harness_adapter_version_invalid
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/e52c3f1e9cecb001.
Report an issue: GitHub.
Appendix: 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 766dce6b13)