mvanhorn/last30days-skill · error · ValueError
field 'source_weights' must be an object when provided
Error message
field 'source_weights' must be an object when provided
What it means
Optional-field shape check in validate_external_plan: source_weights may be omitted (None), but if present it must be a dict mapping source name → numeric weight. Passing a list of pairs, an array, or a string fails here before individual entries are examined.
Source
Thrown at skills/last30days/scripts/lib/planner.py:180
def validate_external_plan(raw: dict) -> None:
"""Validate explicit-plan structure before permissive sanitization.
Enum-like metadata stays permissive because direct pipeline callers rely on
the sanitizer to canonicalize those values.
"""
if not isinstance(raw, dict):
raise ValueError("top-level plan must be an object")
for field in ("intent", "freshness_mode", "cluster_mode", "subqueries"):
if field not in raw:
raise ValueError(f"missing required field '{field}'")
for field in ("intent", "freshness_mode", "cluster_mode"):
if not isinstance(raw[field], str) or not raw[field].strip():
raise ValueError(f"field '{field}' must be a non-empty string")
source_weights = raw.get("source_weights")
if source_weights is not None and not isinstance(source_weights, dict):
raise ValueError("field 'source_weights' must be an object when provided")
for source, weight in (source_weights or {}).items():
if (
not isinstance(source, str)
or not source.strip()
or isinstance(weight, bool)
or not isinstance(weight, (int, float))
):
raise ValueError("field 'source_weights' must map source names to numbers")
subqueries = raw["subqueries"]
if not isinstance(subqueries, list) or not subqueries:
raise ValueError("field 'subqueries' must be a non-empty array")
for index, subquery in enumerate(subqueries):
if not isinstance(subquery, dict):
raise ValueError(f"subqueries[{index}] must be an object")
for field in ("search_query", "ranking_query"):
if not isinstance(subquery.get(field), str) or not subquery[field].strip():
raise ValueError(f"subqueries[{index}].{field} must be a non-empty string")
sources = subquery.get("sources")View on GitHub (pinned to c7460f6114)
Solutions
- Use a JSON object: "source_weights": {"reddit": 2, "x": 1}.
- Convert array-of-pairs formats before submission: dict(pairs).
- Or omit source_weights entirely — it is optional.
Example fix
# before
"source_weights": [{"source": "reddit", "weight": 2}]
# after
"source_weights": {"reddit": 2} Defensive patterns
Strategy: type-guard
Validate before calling
sw = plan.get("source_weights")
if sw is not None and not isinstance(sw, dict):
plan["source_weights"] = dict(sw) # only if it's an iterable of pairs; else drop Type guard
def is_source_weights_map(v) -> bool:
return v is None or (isinstance(v, dict) and all(isinstance(k, str) and k.strip() for k in v)) Prevention
- Model source_weights as a JSON object keyed by source name, never an array of pairs.
- If the field is unused, omit it entirely — it is optional.
When it happens
Trigger: external_plan includes "source_weights": [["reddit", 2], ["x", 1]] (array of pairs) or "source_weights": "reddit:2" — anything that is not None and not a dict.
Common situations: JSON authors modeling the map as an array of {source, weight} objects; YAML-to-JSON conversions changing the shape; LLM plans inventing a list format.
Related errors
- top-level plan must be an object
- missing required field '{field}'
- field '{field}' must be a non-empty string
- field 'source_weights' must map source names to numbers
- field 'subqueries' must be a non-empty array
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/a6adc83d7dd516d5.
Report an issue: GitHub.