JuliusBrussee/caveman · error

registry output has no valid registry_sha256

Error message

registry output has no valid registry_sha256

What it means

loadTransformRegistry() shells out to the caveman-engine binary ('registry' subcommand, or CAVEMAN_ENGINE_BIN) and validates its JSON stdout. The registry_sha256 field must be a 64-char lowercase hex string; otherwise it throws 'registry output has no valid registry_sha256'. This fail-closed check ensures the registry fingerprint used in lock evidence is a real digest, not missing or malformed output from a broken/incompatible engine.

Solutions

  1. Run 'caveman setup' to install/update the engine binary and its registry
  2. Set CAVEMAN_ENGINE_BIN to the correct caveman-engine binary path if a nonstandard install is used
  3. Check 'caveman-engine registry' manually — if its stdout is not clean JSON with a 64-hex registry_sha256, fix or reinstall the engine

Example fix

// before
export CAVEMAN_ENGINE_BIN=/usr/local/bin/old-engine
// after (after running caveman setup)
export CAVEMAN_ENGINE_BIN=$HOME/.caveman/bin/caveman-engine
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidRegistrySha(value: unknown): value is string {
  return typeof value === "string" && /^[0-9a-f]{64}$/.test(value);
}
// before relying on the engine:
const out = await engineRegistryOutput();
const parsed = JSON.parse(out);
if (!isValidRegistrySha(parsed.registry_sha256)) {
  console.error("engine binary incompatible or corrupt; run caveman setup");
}

Type guard

function hasValidRegistrySha(parsed: unknown): parsed is { registry_sha256: string; capabilities: unknown[] } {
  const p = parsed as { registry_sha256?: unknown };
  return typeof p?.registry_sha256 === "string" && /^[0-9a-f]{64}$/.test(p.registry_sha256);
}

Try / catch

try {
  await loadTransformRegistry();
} catch (error) {
  if ((error as Error).message.includes("no valid registry_sha256")) {
    await runCavemanSetup(); // reinstall/upgrade engine
    return await loadTransformRegistry();
  }
  throw error;
}

Prevention

When it happens

Trigger: Running loadTransformRegistry (called during validLockIdentity/lock checks) when the engine binary outputs JSON whose registry_sha256 is absent, non-string, or does not match /^[0-9a-f]{64}$/ — e.g. an old engine version, an error message on stdout, or a wrapper script printing extra text.

Common situations: CAVEMAN_ENGINE_BIN pointing at an outdated or wrong binary; engine not fully set up (setup not run) so it prints a partial payload; a shell wrapper or version banner polluting stdout; a different tool occupying the 'caveman-engine' name on PATH.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/f3124a80fa709e67. Report an issue: GitHub.

Appendix: source

Thrown at packages/agent/src/cli.ts:1114

  const command = process.env.CAVEMAN_ENGINE_BIN ?? "caveman-engine";
  try {
    const env = buildEngineEnv();
    const invocation = portableInvocation(command, ["registry"], { env });
    const { stdout } = await execFileAsync(invocation.command, [...invocation.args], {
      encoding: "utf8",
      env,
      maxBuffer: 2 << 20,
      timeout: 10_000,
    });
    const parsed = JSON.parse(stdout) as {
      registry_sha256?: unknown;
      capabilities?: Array<{
        transform_id?: unknown;
        eligible_segment_kinds?: unknown;
      }>;
    };
    if (typeof parsed.registry_sha256 !== "string" || !/^[0-9a-f]{64}$/.test(parsed.registry_sha256)) {
      throw new Error("registry output has no valid registry_sha256");
    }
    if (!Array.isArray(parsed.capabilities) || parsed.capabilities.length === 0) {
      throw new Error("registry output has no capabilities");
    }
    const capabilities = parsed.capabilities.map((capability): TransformCapability => {
      if (typeof capability.transform_id !== "string" ||
          !Array.isArray(capability.eligible_segment_kinds) ||
          capability.eligible_segment_kinds.some((kind) => typeof kind !== "string")) {
        throw new Error("registry output has invalid capability");
      }
      return {
        transformID: capability.transform_id,
        segmentKinds: capability.eligible_segment_kinds as TransformCapability["segmentKinds"],
      };
    });
    return { sha256: parsed.registry_sha256, capabilities };
  } catch (error) {
    throw new Error(

View on GitHub (pinned to 3ee70a1026)