can1357/oh-my-pi · error · Error
Config overlay must be a YAML mapping: ${filePath}
Error message
Config overlay must be a YAML mapping: ${filePath} What it means
After successful YAML parsing, Settings requires the overlay document to be a YAML mapping (an object). If the document parses to an array, a scalar, or any non-object value, the loader refuses it because overlays are merged as key/value maps. This keeps partial-application semantics well-defined.
Source
Thrown at packages/coding-agent/src/config/settings.ts:1601
let content: string;
try {
content = await Bun.file(filePath).text();
} catch (error) {
throw new Error(
isEnoent(error)
? `Config overlay not found: ${filePath}`
: `Failed to read config overlay ${filePath}: ${String(error)}`,
);
}
let parsed: unknown;
try {
parsed = YAML.parse(content);
} catch (error) {
throw new Error(`Failed to parse config overlay ${filePath}: ${String(error)}`);
}
if (parsed === null || parsed === undefined) return {};
if (typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error(`Config overlay must be a YAML mapping: ${filePath}`);
}
return this.#migrateRawSettings(parsed as RawSettings, captureLegacyChangelogVersion);
}
async #migrateFromLegacy(): Promise<void> {
if (!this.#configPath) return;
let settings: RawSettings = {};
let migrated = false;
// 1. Migrate from settings.json
const settingsJsonPath = path.join(this.#agentDir, "settings.json");
try {
const parsed: unknown = JSONC.parse(await Bun.file(settingsJsonPath).text());
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
settings = this.#deepMerge(settings, this.#migrateRawSettings(parsed as RawSettings));
migrated = true;
try {View on GitHub (pinned to 9690622007)
Solutions
- Wrap the overlay content in a top-level mapping: `key:\n subkey: value` instead of a list or bare value.
- If the file should be empty, empty it entirely (null/empty documents are tolerated and treated as {}).
- Check that you didn't lose the top-level keys when editing — move list items under a mapping key.
Example fix
// before (parses as an array) - model: gpt-5 - agent: build // after overrides: model: gpt-5 agent: build
Defensive patterns
Strategy: validation
Validate before calling
const doc = YAML.parse(await readFile(overlayPath, "utf8"));
if (doc !== null && (typeof doc !== "object" || Array.isArray(doc))) {
throw new Error(`${overlayPath} must contain a top-level YAML mapping`);
} Type guard
function isMapping(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
} Prevention
- Always keep at least one top-level `key:` mapping in overlay files.
- Use empty file (or only comments) to mean 'no overrides' — empty/null is accepted.
- Schema-check overlays against your settings keys before loading.
When it happens
Trigger: An overlay file whose top level is a YAML list (e.g. `- a\n- b`), a bare string/number, or a document that resolves to only a scalar; also YAML files containing just a comment or `---` with no mapping.
Common situations: Someone pasted an array of settings entries instead of an object; a user wrote `settings: ...` content at the wrong nesting level leaving only a scalar; an empty file plus a stray `-` list item.
Related errors
- Invalid agent field: ${filePath}\n${content}
- Failed to load tree-sitter language: {err}
- Destination option ${key} must be a string
- Destination option ${key} must be a finite number
- Destination option ${key} must be a boolean
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/83c861ba832847b3.
Report an issue: GitHub.