mvanhorn/last30days-skill · error · ValueError

field '{field}' must be a non-empty string

Error message

field '{field}' must be a non-empty string

What it means

Type/format check in validate_external_plan for the three string metadata fields (intent, freshness_mode, cluster_mode): each must be a str AND non-empty after strip(). Whitespace-only strings also fail. Values are not yet checked against valid enums here — that canonicalization is the sanitizer's job — but they must at least be real, non-empty strings.

Source

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

    "jobs": {"jobs", "company_signal", "link"},
    "corpus": {"reference", "analysis"},
}


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")

View on GitHub (pinned to c7460f6114)

Solutions

  1. Set each of intent/freshness_mode/cluster_mode to a real non-empty string (e.g. "factual", "recent", "thematic").
  2. If a value came back as null from a generator, default it before submission.
  3. Strip values and assert truthiness in your plan builder.

Example fix

# before
{"intent": " ", "freshness_mode": None, "cluster_mode": "thematic", "subqueries": [...]}

# after
{"intent": "factual", "freshness_mode": "recent", "cluster_mode": "thematic", "subqueries": [...]}
Defensive patterns

Strategy: type-guard

Validate before calling

for f in ("intent", "freshness_mode", "cluster_mode"):
    v = plan.get(f)
    if not isinstance(v, str) or not v.strip():
        plan[f] = DEFAULTS[f]  # or raise before the pipeline does

Type guard

def is_nonempty_str(v) -> bool:
    return isinstance(v, str) and bool(v.strip())

Try / catch

try:
    planner.validate_external_plan(plan)
except ValueError as exc:
    if "must be a non-empty string" in str(exc):
        raise PlanInputError(f"metadata fields must be strings: {exc}") from exc
    raise

Prevention

When it happens

Trigger: Passing intent: 123 or intent: null; passing " " (whitespace-only); passing an empty string "" for any of the three metadata fields.

Common situations: LLM-generated plans emitting null for optional-looking fields; templates with placeholder spaces; numbers or booleans where a string mode was expected (e.g. freshness_mode: 7 instead of "recent").

Related errors


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