libnyanpasu/clash-nyanpasu · warning

overlay document is not a mapping, skipped

Error message

overlay document is not a mapping, skipped

What it means

apply_overlay expects the overlay document to be a mapping (object) whose keys name config fields. If the parsed overlay value is not an object (list, scalar, null), the entire overlay is skipped with this warning rather than erroring, and the config is returned unmodified.

Source

Thrown at backend/nyanpasu-config/src/runtime/executor/overlay.rs:22

use std::sync::Arc;

use crate::runtime::value::ConfigValue;

use super::{
    artifact::StepLogEntry,
    ports::ScriptRunner,
    value_util::{deep_merge_value, get_at, parse_dotted_path, remove_at, replace_at},
};

pub(super) fn apply_overlay(
    overlay: &ConfigValue,
    mut config: ConfigValue,
    runner: &dyn ScriptRunner,
    logs: &mut Vec<StepLogEntry>,
) -> ConfigValue {
    let Some(entries) = overlay.as_object_arc() else {
        logs.push(StepLogEntry::warn(
            "overlay document is not a mapping, skipped",
        ));
        return config;
    };

    // IndexMap iteration = document order (parity with Mapping iteration).
    for (key, value) in entries.iter() {
        // Legacy quirk kept verbatim (merge.rs:248): directive matching and
        // the remainder path are lowercased; bare keys preserve case.
        let lowered = key.to_ascii_lowercase();
        if let Some(field) = strip_any(&lowered, &["prepend__", "prepend-"]) {
            config = splice_sequence(config, field, value, true, logs);
        } else if let Some(field) = strip_any(&lowered, &["append__", "append-"]) {
            config = splice_sequence(config, field, value, false, logs);
        } else if let Some(field) = lowered.strip_prefix("override__") {
            config = override_path(config, field, value, logs);
        } else if let Some(field) = lowered.strip_prefix("filter__") {
            config = filter_path(config, field, value, runner, logs);

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Inspect the overlay document and wrap its content in a top-level mapping of field → operation
  2. Validate the overlay parses to an object before applying
  3. Re-export/re-save the overlay from the UI
  4. Confirm the correct file is wired as the overlay input

Example fix

# before (overlay.yaml)
- rules:
    prepend: []
# after
rules:
  prepend: []
Defensive patterns

Strategy: type-guard

Validate before calling

fn overlay_is_mapping(overlay: &ConfigValue) -> bool { overlay.as_object_arc().is_some() }

Type guard

if !overlay_is_mapping(&overlay) { return Err(anyhow!("overlay must be a top-level mapping")); }

Try / catch

let Some(entries) = overlay.as_object_arc() else {
    return Err(anyhow!("overlay document is not a mapping"));
};

Prevention

When it happens

Trigger: apply_with → apply_overlay: overlay.as_object_arc() returns None because the overlay file/value deserialized to a non-mapping ConfigValue.

Common situations: An overlay file whose top level is a YAML list or a bare scalar; a mis-serialized overlay (e.g. saved as `[...]` instead of `{...}`); selecting the wrong file as an overlay; an overlay that failed parsing and fell back to a non-object default.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/dbc6639fe23aa859. Report an issue: GitHub.