HKUDS/Vibe-Trading · error · RuntimeError
EDGAR full-text search returned no hits block: {message or p
Error message
EDGAR full-text search returned no hits block: {message or payload} What it means
_fts queries the SEC EDGAR full-text search API and expects an Elasticsearch-style {'hits': {...}} dict. If the payload is not a dict, or the 'hits' key is missing/not a dict (e.g. an error envelope with errorMessage), this RuntimeError is raised with the SEC-provided message or raw payload. It typically reflects upstream API errors, rate limiting, or a changed response schema.
Source
Thrown at agent/src/tools/institutional_holdings_tool.py:283
params: Query parameters (``q`` / ``forms`` / ``ciks`` / ``entityName`` /
``startdt`` / ``enddt`` / ``from``).
Returns:
``{"total": int, "total_is_lower_bound": bool, "hits": [source-dicts
with an extra "_id" key]}``. Elasticsearch stops counting at 10,000 and
reports ``relation: "gte"``, so a total at that ceiling is a floor and
must never be presented as an exact count.
Raises:
RuntimeError: When the response carries no ``hits`` block — full-text
search answers HTTP 200 with an error document when it is unhappy.
requests.RequestException: On transport failure.
"""
payload = _sec_get(_FTS_URL, {"q": "", **params}).json()
hits = payload.get("hits") if isinstance(payload, dict) else None
if not isinstance(hits, dict):
message = payload.get("errorMessage") if isinstance(payload, dict) else None
raise RuntimeError(f"EDGAR full-text search returned no hits block: {message or payload}")
rows: List[Dict[str, Any]] = []
for hit in hits.get("hits") or []:
source = hit.get("_source") if isinstance(hit, dict) else None
if isinstance(source, dict):
rows.append({**source, "_id": hit.get("_id")})
total = hits.get("total") if isinstance(hits.get("total"), dict) else {}
return {
"total": int(total.get("value") or 0),
"total_is_lower_bound": total.get("relation") == "gte",
"hits": rows,
}
def _positive(value: float, default: float) -> float:
"""Return *value* when it is positive, else *default*.
The config layer already drops unparseable overrides; this only guards
against a syntactically valid but nonsensical zero or negative bound.View on GitHub (pinned to 80ffdda44c)
Solutions
- Verify the request sends a proper SEC-required User-Agent identifying your app
- Retry with exponential backoff — SEC rate limits are often transient
- Log the payload; if it shows a schema change, update the hits parsing in _fts
Defensive patterns
Strategy: retry
Try / catch
for attempt in range(3):
try:
rows = _fts(params)
break
except RuntimeError as e:
if "no hits block" in str(e) and attempt < 2:
time.sleep(2 ** attempt)
continue
raise Prevention
- Set a descriptive SEC User-Agent header
- Cache FTS results to stay under rate limits
- Monitor payload shape; alert on errorMessage envelopes
When it happens
Trigger: SEC returns 403/429 HTML-error JSON, an {'errorMessage': ...} body, or the FTS endpoint changes shape; also transient SEC outages. Called via _resolve_manager, _list_13f_filings, _ticker_holders, _top_managers.
Common situations: Missing/stale SEC User-Agent header causing 403; heavy polling triggering rate limits; schema drift in EDGAR FTS.
Related errors
- EDGAR filing feed did not parse: {exc}
- Trading 212 API returned HTTP {response.status_code}: {_erro
- SSE connection failed with status {response.status_code}
- WeChat session paused, {remaining_min} min remaining (errcod
- WeChat send text error (ret={ret}, errcode={errcode}): {data
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/47d206e05f3a0156.
Report an issue: GitHub.