JuliusBrussee/caveman · error

Hermes plugins.enabled uses inline YAML; refusing unsafe nat

Error message

Hermes plugins.enabled uses inline YAML; refusing unsafe native merge

What it means

hermesNativeConfig() needs to append its plugin name to `plugins.enabled`, which it can only do as a YAML block list (`enabled:` followed by `- name` lines). An inline flow-style array (`enabled: [a, b]`) cannot be safely line-merged, so it throws and asks the user to expand it first.

Source

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

  if (new Set(routeIndexes.map((index) => lines[index]!.match(/^  ([^:]+):/)?.[1])).size !== routeIndexes.length) {
    throw new Error("Hermes model provider/base_url keys are duplicated; refusing ambiguous install");
  }
  const insertAt = routeIndexes[0] ?? model.start + 1;
  for (const index of [...routeIndexes].sort((a, b) => b - a)) lines.splice(index, 1);
  const routeBlock = [
    `  ${HERMES_NATIVE_ROUTE_BEGIN}`,
    '  provider: "custom"',
    `  base_url: ${yamlQuote(appendUrlPath(gw, "/w/hermes"))}`,
    `  ${HERMES_NATIVE_ROUTE_END}`,
  ];
  lines.splice(insertAt, 0, ...routeBlock);

  let pluginBlock: string | null = null;
  const plugins = topLevelSection(lines, "plugins");
  if (plugins) {
    let enabled = -1;
    for (let i = plugins.start + 1; i < plugins.end; i++) {
      if (/^  enabled:\s*\[/.test(lines[i]!)) throw new Error("Hermes plugins.enabled uses inline YAML; refusing unsafe native merge");
      if (/^  enabled:\s*(?:#.*)?$/.test(lines[i]!)) { enabled = i; break; }
    }
    if (enabled >= 0) {
      if (!hermesNamedPluginEnabled(source, HERMES_NATIVE_PLUGIN_NAME)) {
        const block = [`    ${HERMES_NATIVE_PLUGIN_BEGIN}`, `    - ${HERMES_NATIVE_PLUGIN_NAME}`, `    ${HERMES_NATIVE_PLUGIN_END}`];
        lines.splice(enabled + 1, 0, ...block);
        pluginBlock = block.join("\n");
      }
    } else {
      const block = [`  ${HERMES_NATIVE_PLUGIN_BEGIN}`, "  enabled:", `    - ${HERMES_NATIVE_PLUGIN_NAME}`, `  ${HERMES_NATIVE_PLUGIN_END}`];
      lines.splice(plugins.start + 1, 0, ...block);
      pluginBlock = block.join("\n");
    }
  } else {
    if (lines.length > 0 && lines[lines.length - 1]!.trim()) lines.push("");
    const block = [HERMES_NATIVE_PLUGIN_BEGIN, "plugins:", "  enabled:", `    - ${HERMES_NATIVE_PLUGIN_NAME}`, HERMES_NATIVE_PLUGIN_END];
    lines.push(...block);
    pluginBlock = block.join("\n");

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Expand plugins.enabled to block-list form in the Hermes config
  2. Re-run the caveman hermes install

Example fix

# before
plugins:
  enabled: [shell, git]

# after
plugins:
  enabled:
    - shell
    - git
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from "node:fs";
function hermesEnabledNotInline(path: string): boolean {
  try { return !readFileSync(path, "utf8").split(/\r?\n/).some((l) => /^  enabled:\s*\[/.test(l)); }
  catch { return true; }
}

Try / catch

try { nativeInstallHermes(); } catch (e) {
  if (e instanceof Error && /plugins.enabled uses inline YAML/.test(e.message)) {
    expandEnabledToBlockList(); nativeInstallHermes();
  } else throw e;
}

Prevention

When it happens

Trigger: Native Hermes install when the config contains a line matching /^ enabled:\s*\[/ inside the plugins: section.

Common situations: Compact hand-written configs using flow-style YAML; snippets copied from docs that use inline arrays; generators emitting flow style.

Related errors


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