JuliusBrussee/caveman · error · Error
cave_harness_upstream_version_invalid
cave_harness_upstream_version_invalid
Error message
cave_harness_upstream_version_invalid
What it means
`createHarnessAdapter` validates `identity.upstreamVersion` against `^[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$`: alphanumeric first character, then up to 127 more of letters, digits, `.`, `_`, `+`, `-`. Unlike adapterVersion it permits `+` and doubles the length budget, but it still rejects empty strings, whitespace, parentheses, and other punctuation. The value is compared against the actually-installed upstream package version later, so a malformed pin fails fast here.
Solutions
- Pass the plain semver/build string without qualifiers (e.g. `"7.0.43"`)
- Keep it under 128 characters and start it with a letter or digit
- Sanitize at config load: trim and strip anything after the first space or parenthesis
Example fix
// before upstreamVersion: `7.0.43 (resolved from npm)` // after upstreamVersion: "7.0.43"
Defensive patterns
Strategy: type-guard
Validate before calling
const UPSTREAM_VERSION_RE = /^[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$/;
const upstreamVersion = rawUpstream.trim().split(' ')[0]; // drop banner qualifiers
if (!UPSTREAM_VERSION_RE.test(upstreamVersion)) {
throw new Error(`upstreamVersion fails identity contract: ${JSON.stringify(rawUpstream)}`);
} Type guard
const UPSTREAM_VERSION_RE = /^[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$/;
function isValidUpstreamVersion(v: unknown): v is string {
return typeof v === 'string' && UPSTREAM_VERSION_RE.test(v);
} Try / catch
if (!isValidUpstreamVersion(identity.upstreamVersion)) {
// sanitize or refuse before the factory call; note the installed-package check
// (assertSupportedUpstream) will separately demand the exact pinned version
}
return createHarnessAdapter(id, { ...identity, upstreamVersion: clean(identity.upstreamVersion) }, contract, invoke); Prevention
- Pass the plain version string (e.g. '7.0.43'), never scraped banners with qualifiers
- Trim and cut at the first whitespace when versions come from external tools
- Keep version strings under 128 characters — don't concatenate digests into them
When it happens
Trigger: `upstreamVersion: "7.0.43 (npm latest)"` (spaces/parens from a registry banner); `upstreamVersion: ""` when the value was never set; a 130+ character composite version string; a leading `.` or `-`.
Common situations: Scraping a version string from a gateway or CLI banner that appends qualifiers; concatenating version+digest into one long string; forgetting to set the field at all in a custom adapter.
Related errors
- cave_harness_adapter_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/b69a72c9f0fd4984.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/adapters.ts:93
}
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));
return Object.freeze({
id,
version: identity.adapterVersion,View on GitHub (pinned to 766dce6b13)