nextlevelbuilder/ui-ux-pro-max-skill · error · ValueError
decision rules must be a JSON object
Error message
decision rules must be a JSON object
What it means
After successful JSON parsing, parse_decision_rules requires the top-level value to be a dict. If the JSON is valid but is an array, string, number, or null, this ValueError fires. The grammar is condition-name -> action-array, so any other top-level shape is a contract violation.
Source
Thrown at src/ui-ux-pro-max/scripts/reasoning_contract.py:74
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:
_validate_action(action)
if len(actions) != len(set(actions)):
raise ValueError("{} contains duplicate actions".format(condition))
return rules
def _validate_action(action):
if not isinstance(action, str) or ":" not in action:
raise ValueError("action must use a known prefix: {}".format(action))
prefix, value = action.split(":", 1)
if prefix not in ACTION_PREFIXES:
raise ValueError("unknown decision-rule action: {}".format(action))View on GitHub (pinned to a38d04c3d5)
Solutions
- Wrap the payload as an object keyed by condition: `{"must_have": [...]}` or `{"if_mobile": [...]}`.
- Fix the producer to serialize the rules dict, not a list of actions.
- Add an assertion/isinstance check at the call site before passing user-supplied JSON to parse_decision_rules.
Example fix
# before
rules = parse_decision_rules(json.dumps(["mode:dark"]))
# after
rules = parse_decision_rules(json.dumps({"must_have": ["mode:dark"]})) Defensive patterns
Strategy: type-guard
Validate before calling
import json
parsed = json.loads(raw)
if not isinstance(parsed, dict):
raise ValueError(f"decision rules must be an object, got {type(parsed).__name__}") Type guard
def is_rules_object(parsed) -> bool:
return isinstance(parsed, dict) and all(isinstance(v, list) and v for v in parsed.values()) Try / catch
try:
rules = rc.parse_decision_rules(raw)
except ValueError as exc:
if 'must be a JSON object' in str(exc):
parsed = json.loads(raw)
raw = json.dumps({"must_have": parsed if isinstance(parsed, list) else [parsed]})
rules = rc.parse_decision_rules(raw) # only if this reshape matches your intent Prevention
- Serialize the whole rules dict from typed Python data instead of piecing JSON together.
- Assert isinstance(payload, dict) at the boundary where rules enter your pipeline.
When it happens
Trigger: parse_decision_rules('["mode:dark"]') — a bare action array instead of an object; parse_decision_rules('"if_mobile"'); parse_decision_rules('null') (note: raw=None is coerced to '{}', but the literal string 'null' parses to None and fails this check).
Common situations: A caller stores only the actions list and forgets the condition wrapper; downstream code JSON-encodes a list variable that was supposed to be a rules dict.
Related errors
- duplicate decision-rule key: {}
- {} must map to a non-empty action array
- {} contains duplicate actions
- Invalid JSON file {path}: {error}
- invalid decision-rule JSON: {}
AI-assisted analysis of nextlevelbuilder/ui-ux-pro-max-skill@a38d04c3d5 (2026-08-14).
Data as JSON: /api/errors/70e840b40bccbf2f.
Report an issue: GitHub.