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
- Expand plugins.enabled to block-list form in the Hermes config
- 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
- Write list keys in block style, not flow style [a, b]
- Avoid one-line compact configs for tools whose configs get line-merged
- Normalize YAML style before invoking installers
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
- Aider config uses inline/scalar read; use block-list form be
- Hermes model provider/base_url keys are duplicated; refusing
- Aider config has duplicate openai-api-base keys; refusing un
- Aider config has duplicate read keys; refusing unsafe merge
- existing Hermes Caveman native block is unjournaled; run `ca
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/6daa02c6bf02da43.
Report an issue: GitHub.