{"record":{"id":"f85af8e07bfce9ad","repo":"mvanhorn/last30days-skill","slug":"subqueries-index-weight-must-be-a-number-when-p","errorCode":null,"errorMessage":"subqueries[{index}].weight must be a number when provided","messagePattern":"subqueries\\[(.+?)\\]\\.weight must be a number when provided","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/planner.py","lineNumber":207,"sourceCode":"    subqueries = raw[\"subqueries\"]\n    if not isinstance(subqueries, list) or not subqueries:\n        raise ValueError(\"field 'subqueries' must be a non-empty array\")\n    for index, subquery in enumerate(subqueries):\n        if not isinstance(subquery, dict):\n            raise ValueError(f\"subqueries[{index}] must be an object\")\n        for field in (\"search_query\", \"ranking_query\"):\n            if not isinstance(subquery.get(field), str) or not subquery[field].strip():\n                raise ValueError(f\"subqueries[{index}].{field} must be a non-empty string\")\n        sources = subquery.get(\"sources\")\n        if not isinstance(sources, list) or not sources or not all(\n            isinstance(source, str) and source.strip() for source in sources\n        ):\n            raise ValueError(f\"subqueries[{index}].sources must be a non-empty string array\")\n        weight = subquery.get(\"weight\")\n        if weight is not None and (\n            isinstance(weight, bool) or not isinstance(weight, (int, float))\n        ):\n            raise ValueError(f\"subqueries[{index}].weight must be a number when provided\")\n\n\nDEFAULT_INTENT_CAPABILITIES = {\n    \"comparison\": {\"discussion\", \"video\", \"web\", \"reference\", \"social\", \"link\", \"market\"},\n    \"how_to\": {\"discussion\", \"video\", \"web\", \"reference\", \"link\"},\n}\n\n\nclass DrillTargetError(ValueError):\n    \"\"\"Raised when a follow-up target cannot be resolved to a report cluster.\"\"\"\n\n    def __init__(self, target: str, clusters: list[schema.Cluster]) -> None:\n        candidates = \", \".join(\n            f\"{index}. {cluster.title}\"\n            for index, cluster in enumerate(clusters, start=1)\n        ) or \"(no clusters in the cached report)\"\n        super().__init__(f\"No cluster matched {target!r}. Available clusters: {candidates}\")\n","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/planner.py#L189-L225","documentation":"Optional per-subquery check: 'weight' may be omitted (None), but if present it must be an int or float and not a bool — the same bool-exclusion trick as source_weights, since Python bools are ints. String numbers, booleans, and null-that-isn't-None-in-Python (e.g. JSON null → None is allowed only by omission semantics; explicit non-numeric values fail) are rejected.","triggerScenarios":"A subquery with \"weight\": \"2\", \"weight\": true, or \"weight\": [1]. Omitting the key or setting null (None) is fine.","commonSituations":"Quoted weights from JSON templating; LLMs emitting true to mean 'weighted'; weight as a string because it came from a CLI argument.","solutions":["Use bare numbers: \"weight\": 2 or \"weight\": 0.5.","Cast CLI/env-provided weights with float() before embedding.","Omit the key entirely for default weighting."],"exampleFix":"# before\n{\"search_query\": \"q\", \"ranking_query\": \"q\", \"sources\": [\"reddit\"], \"weight\": \"2\"}\n\n# after\n{\"search_query\": \"q\", \"ranking_query\": \"q\", \"sources\": [\"reddit\"], \"weight\": 2}","handlingStrategy":"type-guard","validationCode":"w = q.get(\"weight\")\nif isinstance(w, str):\n    q[\"weight\"] = float(w)\nif isinstance(w, bool) or w is None and \"weight\" in q:\n    q.pop(\"weight\", None)  # booleans/null mean 'unweighted' -> omit","typeGuard":"def is_optional_numeric_weight(v) -> bool:\n    return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool))","tryCatchPattern":null,"preventionTips":["Omit weight for default weighting instead of passing true/1/null.","Cast string weights with float() at the boundary (CLI args arrive as strings)."],"tags":["planner","external-plan","validation","json"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}