nexu-io/open-design · error · ValueError

Missing Xiaohongshu API base URL

Error message

Missing Xiaohongshu API base URL

What it means

Raised in xiaohongshu_api.search_feeds when the base_url argument is empty after rstrip('/'). Note env.get_xiaohongshu_api_base always returns a default of http://host.docker.internal:18060 when XIAOHONGSHU_API_BASE is unset, so in the normal pipeline path this ValueError only fires if an explicit empty string is passed (or the env default is deliberately overridden to empty).

Source

Thrown at design-templates/last30days/scripts/lib/xiaohongshu_api.py:77

def _build_note_url(feed_id: str, xsec_token: str) -> str:
    """Build a stable Xiaohongshu note URL."""
    if xsec_token:
        return f"https://www.xiaohongshu.com/explore/{feed_id}?xsec_token={xsec_token}"
    return f"https://www.xiaohongshu.com/explore/{feed_id}"


def search_feeds(
    topic: str,
    from_date: str,
    to_date: str,
    base_url: str,
    depth: str = "default",
) -> List[Dict[str, Any]]:
    """Search Xiaohongshu feeds and normalize to web-item shape."""
    base = (base_url or "").rstrip("/")
    if not base:
        raise ValueError("Missing Xiaohongshu API base URL")

    # Quick login sanity check.
    login = http.get(f"{base}/api/v1/login/status", timeout=8, retries=1)
    is_logged_in = (
        login.get("data", {}).get("is_logged_in")
        if isinstance(login, dict) else False
    )
    if not is_logged_in:
        raise http.HTTPError("Xiaohongshu API reachable but not logged in")

    # API supports filters; use recency-oriented defaults.
    publish_time = "一天内" if depth == "quick" else "一周内" if depth == "default" else "半年内"
    payload = {
        "keyword": topic,
        "filters": {
            "sort_by": "综合",
            "note_type": "不限",
            "publish_time": publish_time,

View on GitHub (pinned to 5be4028344)

Solutions

  1. Let env.get_xiaohongshu_api_base supply its default; do not pass an explicit empty base_url.
  2. If overriding XIAOHONGSHU_API_BASE, set it to a real URL (e.g. http://localhost:18060).
  3. Validate base_url is non-empty before calling search_feeds.

Example fix

# before
xiaohongshu_api.search_feeds(topic, frm, to, base_url='')

# after
from lib import env
xiaohongshu_api.search_feeds(topic, frm, to, env.get_xiaohongshu_api_base(config))
Defensive patterns

Strategy: validation

Validate before calling

def search_feeds_safe(topic, frm, to, base_url, depth="default"):
    base = (base_url or "").rstrip("/")
    if not base:
        raise ValueError("Xiaohongshu API base URL is required")
    return xiaohongshu_api.search_feeds(topic, frm, to, base, depth=depth)

Type guard

def has_base_url(base_url) -> bool:
    return bool(base_url and base_url.strip())

Try / catch

null

Prevention

When it happens

Trigger: Calling search_feeds(..., base_url='') or base_url=None directly. Overriding XIAOHONGSHU_API_BASE to an empty string in config. A custom orchestrator that passes env.get_xiaohongshu_api_base result after stripping it to empty.

Common situations: Programmatic caller that built base_url from an unset env var without the env default. Test that monkeypatched the base to ''. Docker host unreachable is a DIFFERENT error (HTTPError 'reachable but not logged in'), not this one.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/a35a693f706e10f5. Report an issue: GitHub.