mvanhorn/last30days-skill · error · ValueError
subqueries[{index}].sources must be a non-empty string array
Error message
subqueries[{index}].sources must be a non-empty string array What it means
Per-subquery check that 'sources' is a non-empty list of non-blank strings. An empty array, a missing sources key, a comma-separated string like "reddit, x", or a list containing null/whitespace entries all fail. The sources list is what the sanitizer intersects with availability, so it must be well-formed up front.
Source
Thrown at skills/last30days/scripts/lib/planner.py:202
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"},
}
class DrillTargetError(ValueError):
"""Raised when a follow-up target cannot be resolved to a report cluster."""
def __init__(self, target: str, clusters: list[schema.Cluster]) -> None:
candidates = ", ".join(View on GitHub (pinned to c7460f6114)
Solutions
- Always provide an explicit non-empty array: "sources": ["reddit", "hackernews"].
- Split string forms: [s.strip() for s in val.split(',') if s.strip()].
- If 'all sources' is intended, list them explicitly.
Example fix
# before
{"search_query": "q", "ranking_query": "q", "sources": "reddit, x"}
# after
{"search_query": "q", "ranking_query": "q", "sources": ["reddit", "x"]} Defensive patterns
Strategy: type-guard
Validate before calling
srcs = q.get("sources")
if isinstance(srcs, str):
srcs = [s.strip() for s in srcs.split(",") if s.strip()]
q["sources"] = srcs or ["reddit"]
assert q["sources"] and all(isinstance(s, str) and s.strip() for s in q["sources"]) Type guard
def is_source_string_array(v) -> bool:
return isinstance(v, list) and len(v) > 0 and all(isinstance(s, str) and s.strip() for s in v) Prevention
- Always emit sources as a JSON array of names, never a comma-separated string.
- There is no 'all sources' sentinel — list the sources you want explicitly.
When it happens
Trigger: "sources": [] , "sources": "reddit", "sources": ["reddit", " "] , or the key omitted entirely from a subquery object.
Common situations: LLM emitting a comma-separated string instead of an array; generators dropping the key when 'all sources' was intended; empty list meaning 'use defaults' — not supported, sources must be explicit.
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 be an object when provided
- field 'source_weights' must map source names to numbers
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/6927ce8c974d9e5e.
Report an issue: GitHub.