mvanhorn/last30days-skill · error · ValueError

No listing sources are available for global trending

Error message

No listing sources are available for global trending

What it means

ValueError from planner.build_discovery_plan in the GLOBAL trending branch (empty/whitespace domain). After discarding 'x' from the allowed set, it intersects DISCOVERY_SOURCE_ORDER with the allowed sources; if nothing survives, there is no listing source to sweep and discovery cannot proceed. Note this branch explicitly drops 'x' even if the caller allowed it.

Solutions

  1. Include at least one of reddit/hackernews/digg in available_sources when calling build_discovery_plan for global trending.
  2. Prefer the pipeline entry points (run_discover) which pre-filter available sources and raise the earlier, clearer error instead.
  3. If you truly have no listing sources, fix availability first (all three are keyless-capable).

Example fix

# before
plan = planner.build_discovery_plan("", available_sources=["x"])

# after
plan = planner.build_discovery_plan("", available_sources=["reddit", "hackernews"])
Defensive patterns

Strategy: validation

Validate before calling

LISTING = {"reddit", "hackernews", "digg"}
allowed = set(available_sources or []) & LISTING  # 'x' is always discarded here
if domain.strip() == "" and not allowed:
    raise SystemExit("global trending needs >=1 of reddit/hackernews/digg")

Try / catch

try:
    plan = planner.build_discovery_plan("", available_sources=srcs)
except ValueError as exc:
    if "global trending" in str(exc):
        srcs = ["reddit"]
        plan = planner.build_discovery_plan("", available_sources=srcs)
    else:
        raise

Prevention

When it happens

Trigger: build_discovery_plan called with domain='' (or whitespace) and available_sources set to values that exclude all of reddit/hackernews/digg — e.g. available_sources=['x'] (x is discarded) or available_sources=['youtube']. In the real pipeline this is pre-filtered (pipeline.py intersects with DISCOVERY_SOURCES), so hitting it usually means calling the planner directly.

Common situations: Direct planner unit-tests or custom orchestrators passing arbitrary available_sources; passing discovery's available list derived from a keyed-only config where none of the three listing sources are usable; mock runs with restricted sources.

Related errors


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

Appendix: source

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

    An empty domain is global trending: sweep every river feed's own hot list
    (r/all, HN front page, Digg) with no category scoping. Keyword-driven
    sources (X, Techmeme, arXiv - none of which expose a river/front-page
    lane) sit out of the global nominate stage and join per-topic at the
    enrichment pass, where every nomination gets a full research run.
    """
    normalized_domain = " ".join(domain.split())
    if not normalized_domain:
        resolved = [
            subreddit.removeprefix("r/").strip()
            for subreddit in (subreddits or ["all"])
            if subreddit.strip()
        ]
        allowed = set(DISCOVERY_SOURCE_ORDER if available_sources is None else available_sources)
        allowed.discard("x")
        sources = [source for source in DISCOVERY_SOURCE_ORDER if source in allowed]
        if not sources:
            raise ValueError("No listing sources are available for global trending")
        return schema.DiscoveryPlan(
            domain="",
            category=None,
            subreddits=resolved or ["all"],
            sources=sources,
        )

    category = categories.detect_category(normalized_domain)
    candidate_subreddits = list(subreddits or categories.peer_subs_for(category))
    seen_subreddits: set[str] = set()
    resolved_subreddits: list[str] = []
    for subreddit in candidate_subreddits:
        normalized_subreddit = subreddit.removeprefix("r/").strip()
        key = normalized_subreddit.lower()
        if not normalized_subreddit or key in seen_subreddits:
            continue
        seen_subreddits.add(key)
        resolved_subreddits.append(normalized_subreddit)

View on GitHub (pinned to c7460f6114)