nextlevelbuilder/ui-ux-pro-max-skill · error · ValueError

duplicate decision-rule key: {}

Error message

duplicate decision-rule key: {}

What it means

reasoning_contract.py parses decision-rule JSON with object_pairs_hook=_object_without_duplicates, which rejects duplicate keys inside any JSON object. Python's default json.loads silently keeps the last duplicate; this hook instead raises so a rule file can never rely on accidental overwrite semantics. Duplicate condition keys (e.g. two "must_have" entries) are the typical trigger.

Source

Thrown at src/ui-ux-pro-max/scripts/reasoning_contract.py:62

ALLOWED_CONDITIONS = {"must_have", *CONDITION_SIGNALS}
ACTION_PREFIXES = {"constraint", "style", "pattern", "mode"}
TOKEN_ACTION_PREFIXES = {"constraint", "style"}
TOKEN_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
CONDITION_PATTERNS = {
    condition: tuple(
        re.compile(r"(?<!\w)" + re.escape(signal) + r"(?!\w)")
        for signal in signals
    )
    for condition, signals in CONDITION_SIGNALS.items()
}


def _object_without_duplicates(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("duplicate decision-rule key: {}".format(key))
        result[key] = value
    return result


def parse_decision_rules(raw):
    """Parse the canonical condition -> action-array representation."""
    try:
        rules = json.loads(raw or "{}", object_pairs_hook=_object_without_duplicates)
    except json.JSONDecodeError as error:
        raise ValueError("invalid decision-rule JSON: {}".format(error)) from error
    if not isinstance(rules, dict):
        raise ValueError("decision rules must be a JSON object")
    for condition, actions in rules.items():
        if condition not in ALLOWED_CONDITIONS:
            raise ValueError("unknown decision-rule condition: {}".format(condition))
        if not isinstance(actions, list) or not actions:
            raise ValueError("{} must map to a non-empty action array".format(condition))
        for action in actions:

View on GitHub (pinned to a38d04c3d5)

Solutions

  1. Locate the duplicated key named in the message within the decision-rule JSON string and delete one occurrence.
  2. If merging two rule sets, merge Python dicts before serializing (`{**a, **b}`) rather than concatenating JSON text.
  3. Validate with `python3 -c "import json;json.loads(open(f).read(), object_pairs_hook=lambda p: p)"` style tooling, or just call parse_decision_rules on the candidate payload before saving.

Example fix

// before
{"must_have":["mode:dark"],"must_have":["style:neo-brutalism"]}

// after
{"must_have":["mode:dark","style:neo-brutalism"]}
Defensive patterns

Strategy: validation

Validate before calling

import json
def has_duplicate_keys(raw):
    seen = []
    def hook(pairs):
        keys = [k for k, _ in pairs]
        if len(keys) != len(set(keys)):
            seen.extend(k for k in keys if keys.count(k) > 1)
        return dict(pairs)
    json.loads(raw or "{}", object_pairs_hook=hook)
    return sorted(set(seen))

dups = has_duplicate_keys(rule_json)
if dups:
    raise ValueError(f"fix duplicate keys before parse_decision_rules: {dups}")

Try / catch

from src.ui_ux_pro_max.scripts import reasoning_contract as rc
try:
    rules = rc.parse_decision_rules(raw)
except ValueError as exc:
    # all contract errors (syntax, duplicates, vocabulary) arrive as ValueError
    log_and_reject_payload(raw, str(exc))

Prevention

When it happens

Trigger: Calling parse_decision_rules(raw) where raw is a JSON string containing the same object key twice at any level — commonly two `"if_mobile": [...]` entries produced by merging rule files or by a bad copy-paste in the decisionRules column of a CSV/design-system payload.

Common situations: Hand-editing a decisionRules JSON blob in a spreadsheet cell and duplicating a condition; programmatic concatenation of two rule objects via string join instead of dict merge.

Related errors


AI-assisted analysis of nextlevelbuilder/ui-ux-pro-max-skill@a38d04c3d5 (2026-08-14). Data as JSON: /api/errors/bc3c09bbd4db0eb7. Report an issue: GitHub.