JuliusBrussee/caveman · error

registry output has no capabilities

Error message

registry output has no capabilities

What it means

loadTransformRegistry() requires the engine's 'registry' JSON output to contain a non-empty capabilities array. If capabilities is missing, not an array, or empty, it throws 'registry output has no capabilities'. An empty registry means no transform can be routed, so the framework fails closed instead of producing a useless lock/plan.

Solutions

  1. Run 'caveman setup' to install the full engine with its transform registry
  2. Point CAVEMAN_ENGINE_BIN at the official caveman-engine binary that registers transforms
  3. Run 'caveman-engine registry' manually and confirm the JSON contains a non-empty capabilities array

Example fix

// before
"CAVEMAN_ENGINE_BIN": "/tmp/stub-engine"  // prints {"registry_sha256":"..."}
// after
"CAVEMAN_ENGINE_BIN": "/home/me/.caveman/bin/caveman-engine" // full engine
Defensive patterns

Strategy: type-guard

Validate before calling

const parsed = JSON.parse(await engineRegistryOutput());
if (!Array.isArray(parsed.capabilities) || parsed.capabilities.length === 0) {
  console.error("engine registers no transforms; install the full engine via caveman setup");
}

Type guard

function hasCapabilities(parsed: unknown): parsed is { registry_sha256: string; capabilities: unknown[] } {
  const p = parsed as { capabilities?: unknown };
  return Array.isArray(p?.capabilities) && p.capabilities.length > 0;
}

Try / catch

try {
  await loadTransformRegistry();
} catch (error) {
  if ((error as Error).message.includes("no capabilities")) {
    await runCavemanSetup(); // full engine, not a stub build
    return await loadTransformRegistry();
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling loadTransformRegistry (during lock checks or compile) when the engine binary's registry JSON has no capabilities array or an empty one — e.g. an engine build compiled without transforms, or a stub/mocked CAVEMAN_ENGINE_BIN.

Common situations: CAVEMAN_ENGINE_BIN pointing at a minimal/custom engine build with no registered transforms; an engine version that renamed the capabilities field; running a placeholder script during tests.

Related errors


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

Appendix: source

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

    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(
      `cave_transform_registry_unavailable: run caveman setup or set CAVEMAN_ENGINE_BIN (${error instanceof Error ? error.message : String(error)})`,
    );
  }

View on GitHub (pinned to 3ee70a1026)