JuliusBrussee/caveman · error

${path} already exists; refusing to overwrite an unjournaled

Error message

${path} already exists; refusing to overwrite an unjournaled plugin

What it means

hermesNativeMutations() installs a Caveman plugin by creating plugin.yaml and __init__.py inside hermesNativePluginDir(). Unlike the config file, these plugin files carry no ownership marker, so ANY pre-existing file at those paths is treated as unjournaled third-party content and the install refuses to overwrite it.

Source

Thrown at packages/cli/src/index.ts:6775

    owned: {
      route: appendUrlPath(gw, "/w/hermes"),
      route_block: routeBlock.join("\n"),
      previous_route_lines: previousRouteLines,
      plugin_block: pluginBlock,
      mcp_block: mcpBlock,
    },
  };
}

function hermesNativeMutations(gw: string, mcpBinary: string): NativeMutation[] {
  const configPath = hermesConfigPath();
  const configBefore = fileBytes(configPath);
  const native = hermesNativeConfig(configBefore?.toString("utf8") ?? "", gw, mcpBinary);
  const pluginDir = hermesNativePluginDir();
  const manifestPath = join(pluginDir, "plugin.yaml");
  const initPath = join(pluginDir, "__init__.py");
  for (const path of [manifestPath, initPath]) {
    if (fileBytes(path)) throw new Error(`${path} already exists; refusing to overwrite an unjournaled plugin`);
  }
  return [
    { file: configPath, before: configBefore, after: Buffer.from(native.text), kind: "hermes-config", owned: native.owned },
    { file: manifestPath, before: null, after: Buffer.from(hermesNativePluginManifest()), kind: "hermes-plugin-manifest" },
    { file: initPath, before: null, after: Buffer.from(hermesNativePluginSource()), kind: "hermes-plugin-init" },
  ];
}

function withIntegrationLock<T>(agent: string, run: () => T): T {
  const lock = join(cavemanHome(), "integrations", `.lock-${agent}`);
  const ownerPath = join(lock, "owner.json");
  const token = randomUUID();
  mkdirSync(dirname(lock), { recursive: true, mode: 0o700 });
  try { mkdirSync(lock, { recursive: false, mode: 0o700 }); }
  catch (error) {
    if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
    let stale = false;
    try {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Inspect both files; if they are leftovers or your own code, move them aside (mv plugin.yaml plugin.yaml.bak)
  2. If from a previous caveman install, delete them so the current install regenerates them
  3. Re-run the caveman hermes install

Example fix

# before
$ ls ~/.hermes/plugins/caveman/   # plugin.yaml __init__.py already present
Error: .../plugin.yaml already exists; refusing to overwrite an unjournaled plugin

# after
$ mv ~/.hermes/plugins/caveman ~/.hermes/plugins/caveman.bak
$ caveman native install hermes   # recreates manifest and __init__.py
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from "node:fs";
function hermesPluginPathsFree(dir: string): boolean {
  return !existsSync(join(dir, "plugin.yaml")) && !existsSync(join(dir, "__init__.py"));
}

Try / catch

try { nativeInstallHermes(); } catch (e) {
  if (e instanceof Error && /refusing to overwrite an unjournaled plugin/.test(e.message)) {
    backUpAndClearPluginDir(); nativeInstallHermes();
  } else throw e;
}

Prevention

When it happens

Trigger: Native Hermes install when the Hermes plugin directory already contains a plugin.yaml or __init__.py file (any content, any origin).

Common situations: An earlier caveman version or aborted install left files behind; the user authored their own plugin at the same path; a different tool claimed the directory.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/cb6e3654e41360f8. Report an issue: GitHub.