Panniantong/Agent-Reach · error · ValueError

limit must be non-negative

Error message

limit must be non-negative

What it means

XueqiuChannel.search (or the public-timeline helper at agent_reach/channels/xueqiu.py:239) validates its limit argument up front: a negative limit raises ValueError('limit must be non-negative') before any network call. Values 1..50 are accepted (anything above 50 is clamped with min(limit, 50)); 0 short-circuits to an empty list.

Source

Thrown at agent_reach/channels/xueqiu.py:239

                }
            )
        return results

    def get_hot_posts(self, limit: int = 20) -> list:
        """获取雪球热门帖子。

        Uses the v4 public timeline endpoint which returns posts in a `list`
        array.  Each item carries a JSON-encoded `data` field containing the
        actual post payload (title, description, user, like_count, target).

        Args:
            limit: 最多返回条数(上限 50)

        Returns a list of dicts with keys:
          id, title, text, author, likes, url
        """
        if limit < 0:
            raise ValueError("limit must be non-negative")
        limit = min(limit, 50)
        if limit == 0:
            return []
        data = _get_json(
            "https://xueqiu.com/v4/statuses/public_timeline_by_category.json"
            f"?since_id=-1&max_id=-1&count={limit}&category=-1"
        )
        items = data.get("list") or []
        results = []
        for item in items[:limit]:
            # Each item.data is a JSON string containing the real post payload
            try:
                post = (
                    json.loads(item["data"])
                    if isinstance(item.get("data"), str)
                    else {}
                )
            except (json.JSONDecodeError, KeyError):

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Pass a non-negative limit; use 0 for 'nothing' and let the channel clamp anything over 50
  2. If -1 was meant as 'default', pass a concrete value like 20 instead
  3. Fix the arithmetic producing the negative value (e.g. max(0, remaining))
  4. Validate/validate user-supplied counts at your own CLI boundary with argparse type=int plus a min check

Example fix

# before
results = channel.search(query, limit=page_size - offset)  # can go negative

# after
results = channel.search(query, limit=max(0, page_size - offset))
Defensive patterns

Strategy: validation

Validate before calling

def safe_limit(requested: int) -> int:
    """Clamp to the xueqiu channel's accepted range before calling."""
    return max(0, min(requested, 50))

results = channel.search(query, limit=safe_limit(user_count))

Type guard

def is_valid_xueqiu_limit(value: object) -> bool:
    return isinstance(value, int) and not isinstance(value, bool) and value >= 0

Try / catch

try:
    results = channel.search(query, limit=n)
except ValueError as exc:
    if "non-negative" in str(exc):
        results = channel.search(query, limit=abs(n))  # or reject input upstream
    else:
        raise

Prevention

When it happens

Trigger: Calling the public timeline API with limit=-1 (e.g. code that computes limit as size - remaining and underflows, or treats -1 as 'default' like some APIs do). Note: the check is limit < 0, so limit=0 is valid and returns [].

Common situations: Porting code from APIs where -1 means 'server default' (xueqiu's own web API uses -1 sentinels for since_id/max_id); arithmetic that computes a negative count (10 - 20); passing an unvalidated CLI argument straight through.

Related errors


AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14). Data as JSON: /api/errors/76231db784765f63. Report an issue: GitHub.