mvanhorn/last30days-skill · error · ValueError

subqueries[ ] must be an object

Error message

subqueries[{index}] must be an object

What it means

Per-item check in validate_external_plan: each element of subqueries must itself be a dict/object. A bare string query, null entry, or nested list fails with the offending index in the message, making it easy to locate the bad element.

Solutions

  1. Wrap bare strings into objects: {"search_query": s, "ranking_query": s, "sources": ["reddit"]}.
  2. Filter out None entries before submission: [q for q in subqueries if q].
  3. Check the index named in the error message first.

Example fix

# before
"subqueries": ["rust compilers 2026"]

# after
"subqueries": [{"search_query": "rust compilers 2026", "ranking_query": "rust compiler release", "sources": ["reddit"]}]
Defensive patterns

Strategy: validation

Validate before calling

plan["subqueries"] = [q for q in plan["subqueries"] if isinstance(q, dict)]
if not plan["subqueries"]:
    raise SystemExit("no valid subquery objects after filtering")

Type guard

def is_subquery_object(v) -> bool:
    return isinstance(v, dict)

Prevention

When it happens

Trigger: "subqueries": ["rust compilers", {...}] — the first element is a string, not an object; or a null element from a generator that skipped an item.

Common situations: LLMs sometimes emit shorthand string queries instead of full objects; list filtering leaves None placeholders; copy-paste of a query string into the array.

Related errors


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/a0465c3dbf36da72. Report an issue: GitHub.

Appendix: source

Thrown at skills/last30days/scripts/lib/planner.py:194

            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")
        if not isinstance(sources, list) or not sources or not all(
            isinstance(source, str) and source.strip() for source in sources
        ):
            raise ValueError(f"subqueries[{index}].sources must be a non-empty string array")
        weight = subquery.get("weight")
        if weight is not None and (
            isinstance(weight, bool) or not isinstance(weight, (int, float))
        ):
            raise ValueError(f"subqueries[{index}].weight must be a number when provided")


DEFAULT_INTENT_CAPABILITIES = {
    "comparison": {"discussion", "video", "web", "reference", "social", "link", "market"},
    "how_to": {"discussion", "video", "web", "reference", "link"},

View on GitHub (pinned to c7460f6114)