JuliusBrussee/caveman · error · Error
cave_harness_adapter_version_invalid
cave_harness_adapter_version_invalid
Error message
cave_harness_adapter_version_invalid
What it means
`createHarnessAdapter` validates `identity.adapterVersion` against `^[0-9A-Za-z][0-9A-Za-z._-]{0,63}$` before building the frozen adapter manifest. The string must start with an alphanumeric character, contain only letters, digits, `.`, `_`, `-`, and be at most 64 characters — note `+` is NOT allowed here even though it is allowed for `upstreamVersion`. Because the value feeds the contract SHA256 evidence chain, any other shape throws at construction time.
Solutions
- Strip `+build` metadata or replace `+` with `-` before constructing the identity
- Ensure the first character is alphanumeric and total length is at most 64
- Add the same regex as a build-time assert on the version source (package.json field, env var)
Example fix
// before
createVercelAISDKAdapter({ adapterVersion: pkgVersion /* "1.0.0+sha.9f2a1c" */, ... })
// after
createVercelAISDKAdapter({ adapterVersion: pkgVersion.split('+')[0], ... }) Defensive patterns
Strategy: type-guard
Validate before calling
const ADAPTER_VERSION_RE = /^[0-9A-Za-z][0-9A-Za-z._-]{0,63}$/;
const adapterVersion = pkgVersion.split('+')[0];
if (!ADAPTER_VERSION_RE.test(adapterVersion)) {
throw new Error(`adapterVersion fails identity contract: ${JSON.stringify(adapterVersion)}`);
}
return createVercelAISDKAdapter({ adapterVersion, ...identity }, agent); Type guard
const ADAPTER_VERSION_RE = /^[0-9A-Za-z][0-9A-Za-z._-]{0,63}$/;
function isValidAdapterVersion(v: unknown): v is string {
return typeof v === 'string' && ADAPTER_VERSION_RE.test(v);
} Try / catch
try {
return createHarnessAdapter(id, identity, contract, invoke);
} catch (e) {
if (e instanceof Error && e.message === 'cave_harness_adapter_version_invalid') {
throw new Error(`build produced an adapterVersion outside [A-Za-z0-9._-]{1,64} (leading alnum): check your version source`);
}
throw e;
} Prevention
- Derive adapterVersion from a package.json-style semver and strip `+build` metadata
- Add a unit test asserting your release version string matches the contract regex
- Fail at build time, not adapter-construction time, so bad versions never ship
When it happens
Trigger: `createVercelAISDKAdapter({ adapterVersion: "1.2.3+build.42", ... })`; an empty adapterVersion from a missing build env var; a leading `-` or `.`; a version string longer than 64 characters (e.g. prefixed full digest); whitespace anywhere in the string.
Common situations: Reusing a semver string with build metadata (`+sha...`) as the adapter version; CI injecting `VERSION` with trailing newline or spaces; deriving the version from `git describe --dirty` with characters outside the allowed set; hash-based versions exceeding the 64-char cap.
Related errors
- cave_harness_upstream_version_invalid
- cave_harness_request_invalid
- artifactId is required
- assemble requires model and sessionId
- cave artifact response missing artifact_id
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/46e50833e658b55f.
Report an issue: GitHub.
Appendix: 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 766dce6b13)