libnyanpasu/clash-nyanpasu · warning

filter `merge` target item is not a mapping, item kept

Error message

filter `merge` target item is not a mapping, item kept

What it means

When a filter's `merge` action applies to an item, the item must be a mapping; if it is a scalar or array the merge cannot proceed. Unlike the legacy implementation (which panicked via as_mapping_mut().unwrap()), this executor keeps the item and logs a warning instead.

Source

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

                        return item;
                    }
                    match &action {
                        FilterAction::Expr(expr) => match runner.eval_item_expr(expr, &item) {
                            Ok(next) => next,
                            Err(error) => {
                                logs.push(StepLogEntry::warn(format!(
                                    "filter `expr` failed, item kept: {error}"
                                )));
                                item
                            }
                        },
                        FilterAction::Override(replacement) => (*replacement).clone(),
                        FilterAction::Merge(merge) => {
                            // Legacy panics on non-mapping items (merge.rs:163
                            // `as_mapping_mut().unwrap()`); never-fail keeps
                            // the item instead (spec §13 #15).
                            if item.as_object_arc().is_none() {
                                logs.push(StepLogEntry::warn(
                                    "filter `merge` target item is not a mapping, item kept",
                                ));
                                return item;
                            }
                            deep_merge_value(Some(&item), merge)
                        }
                        FilterAction::Remove(paths) => remove_from_item(item, paths, logs),
                    }
                })
                .collect()
        }
        _ => {
            logs.push(StepLogEntry::warn("invalid filter value, skipped"));
            items
        }
    }
}

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Restrict the `when` predicate so it only matches mapping items (e.g. check item.type or another object-only field)
  2. Change the action from `merge` to `override` if replacing the item wholesale is acceptable
  3. Convert sequence items into mappings before filtering
  4. Ignore the warning if keeping unmatched non-mapping items is intended

Example fix

// before
when: "true"
merge: { extra: 1 }
// after
when: "item.name != null"
merge: { extra: 1 }
Defensive patterns

Strategy: validation

Validate before calling

// in the `when` predicate, restrict to mapping items:
// when: "item.name != null"
let matched: Vec<_> = items.iter().filter(|i| i.as_object_arc().is_some()).collect();
assert!(!matched.is_empty(), "merge action requires mapping items");

Type guard

fn item_is_mapping(v: &ConfigValue) -> bool { v.as_object_arc().is_some() }

Prevention

When it happens

Trigger: A `when` predicate matches a non-mapping item in the filtered sequence and the action is `merge`.

Common situations: Filtering lists that mix scalars and objects (e.g. rules as plain strings) with a merge action intended for object items.

Related errors


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