mvanhorn/last30days-skill · error · ValueError

StockTwits item has no symbol

Error message

StockTwits item has no symbol

What it means

ValueError raised in StockTwits refetch_datum when the symbol needed to rebuild the stream URL cannot be found - both item.metadata['symbol'] and item.container are empty after strip/upper. Without a symbol there is no /streams/symbol/<S>.json endpoint to query, so re-fetch is impossible.

Source

Thrown at skills/last30days/scripts/lib/stocktwits.py:310

    tagged = bull + bear
    return {
        "bullish": bull,
        "bearish": bear,
        "untagged": len(messages) - tagged,
        "pct_bullish": round(100 * bull / tagged) if tagged else None,
        "sample": len(messages),
    }


def refetch_datum(item: Any, datum_key: str) -> dict[str, Any]:
    """Re-fetch the same paginated, date-filtered symbol-stream population."""
    from . import http

    if datum_key != "pct_bullish":
        raise KeyError(f"Unsupported StockTwits datum: {datum_key}")
    symbol = str(item.metadata.get("symbol") or item.container or "").strip().upper()
    if not symbol:
        raise ValueError("StockTwits item has no symbol")
    url = _STREAM_URL.format(symbol=urllib.parse.quote(symbol))
    window = item.metadata.get("freshness_window") or {}
    depth = str(window.get("depth") or "default")
    target = _DEPTH.get(depth, _DEPTH["default"])
    messages: list[dict[str, Any]] = []
    cursor_max = None
    while len(messages) < target:
        request_kwargs: dict[str, Any] = {"timeout": 10, "retries": 2}
        if cursor_max:
            request_kwargs["params"] = {"max": cursor_max}
        data = http.request("GET", url, **request_kwargs)
        if not isinstance(data, dict) or not isinstance(data.get("messages"), list):
            raise KeyError("StockTwits symbol stream was not returned")
        batch = data["messages"]
        if not batch:
            break
        messages.extend(batch)
        cursor = data.get("cursor") or {}

View on GitHub (pinned to c7460f6114)

Solutions

  1. Ensure ingest stores the resolved symbol in item.metadata['symbol']
  2. Guard before calling: skip refetch when neither metadata symbol nor container is non-empty
  3. Re-run search_stocktwits for the topic to rebuild a fully-populated item

Example fix

# before
datum = refetch_datum(item, "pct_bullish")  # no symbol anywhere

# after
symbol = (item.metadata.get("symbol") or item.container or "").strip().upper()
if not symbol:
    skip(item)
else:
    datum = refetch_datum(item, "pct_bullish")
Defensive patterns

Strategy: validation

Validate before calling

symbol = str(item.metadata.get("symbol") or getattr(item, "container", "") or "").strip().upper()
if not symbol:
    skip_refetch(item)  # cannot rebuild the stream URL without a symbol

Type guard

def item_has_stocktwits_symbol(item) -> bool:
    return bool(
        str(item.metadata.get("symbol") or getattr(item, "container", "") or "").strip()
    )

Try / catch

try:
    datum = refetch_datum(item, "pct_bullish")
except ValueError as e:
    if "no symbol" in str(e):
        skip_refetch(item)  # data-quality gap; re-ingest, don't retry

Prevention

When it happens

Trigger: refetch_datum(item, 'pct_bullish') on an item whose metadata dict lacks 'symbol' and whose container attribute is empty/None - typically items constructed manually or deserialized from a schema that dropped metadata.

Common situations: Persisted items losing metadata across schema migrations; test fixtures built without metadata; items created via parse paths that only set container sometimes; refetch attempted on non-symbol (watchlist) items.

Related errors


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