mvanhorn/last30days-skill · error · ValueError

No listing sources are available for {normalized_domain!r}

Error message

No listing sources are available for {normalized_domain!r}

What it means

Same guard as the global branch, but for a non-empty domain: build_discovery_plan intersects DISCOVERY_SOURCE_ORDER with available_sources and raises when the intersection is empty. Unlike the global branch, 'x' is NOT discarded here, but 'x' is also not in DISCOVERY_SOURCE_ORDER, so passing only non-listing sources still triggers it. The domain is echoed with !r for easy debugging.

Source

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

    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)
    # The curated map intentionally stays small. Keep discovery's keyless floor
    # for uncategorized domains by sweeping r/all and applying domain relevance
    # during normalization instead of inventing a second category resolver.
    if not resolved_subreddits:
        resolved_subreddits = ["all"]

    allowed = set(DISCOVERY_SOURCE_ORDER if available_sources is None else available_sources)
    sources = [source for source in DISCOVERY_SOURCE_ORDER if source in allowed]
    if not sources:
        raise ValueError(f"No listing sources are available for {normalized_domain!r}")

    return schema.DiscoveryPlan(
        domain=normalized_domain,
        category=category,
        subreddits=resolved_subreddits,
        sources=sources,
    )

ALLOWED_INTENTS = {
    "factual",
    "product",
    "concept",
    "opinion",
    "how_to",
    "comparison",
    "breaking_news",
    "prediction",
}

View on GitHub (pinned to c7460f6114)

Solutions

  1. Pass at least one listing source (reddit/hackernews/digg) in available_sources.
  2. Use run_discover instead of the planner directly — it computes availability and filters requested sources correctly.
  3. If this surfaces from the pipeline path, treat it as 'no listing source currently available' and fix credentials/PATH for digg if that was the only one requested.

Example fix

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

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

Strategy: validation

Validate before calling

LISTING = {"reddit", "hackernews", "digg"}
if not (set(available_sources or []) & LISTING):
    available_sources = list(LISTING)  # or raise explicitly
plan = planner.build_discovery_plan(domain, available_sources=available_sources)

Try / catch

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

Prevention

When it happens

Trigger: build_discovery_plan(domain='example topic', available_sources=['youtube']) or available_sources=[] — the ordered intersection [s for s in DISCOVERY_SOURCE_ORDER if s in allowed] is empty and the ValueError fires with the domain repr.

Common situations: Custom callers/tests passing restricted or empty available_sources; configs where every listing source was filtered out upstream (e.g. all failed availability checks); passing search-style sources to the discovery planner.

Related errors


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