ZhuLinsen/daily_stock_analysis · error · ValueError

{field_name} must be a number

Error message

{field_name} must be a number

What it means

ValueError from DecisionSignalService._optional_float (src/services/decision_signal_service.py:1247): an optional numeric field (confidence, prices, etc.) was present but float(value) raised TypeError/ValueError — the value is not coercible to a number. Note float() accepts numeric strings ('0.8', '1e-3'), so failures mean genuinely non-numeric content: letters, nested objects, lists, or types without a numeric conversion.

Source

Thrown at src/services/decision_signal_service.py:1247

        return text

    @staticmethod
    def _optional_signal_text(value: Any) -> Optional[str]:
        if value is None:
            return None
        if isinstance(value, (dict, list)):
            return json.dumps(sanitize_decision_signal_payload(value), ensure_ascii=False, sort_keys=True)
        text = sanitize_decision_signal_text(value)
        return text or None

    @staticmethod
    def _optional_float(value: Any, field_name: str) -> Optional[float]:
        if value in (None, ""):
            return None
        try:
            return float(value)
        except (TypeError, ValueError) as exc:
            raise ValueError(f"{field_name} must be a number") from exc

    @classmethod
    def _optional_price_float(cls, value: Any, field_name: str) -> Optional[float]:
        number = cls._optional_float(value, field_name)
        if number is None:
            return None
        if not math.isfinite(number) or number <= 0:
            raise ValueError(f"{field_name} must be a finite positive number")
        return number

    @staticmethod
    def _validate_entry_range(fields: Dict[str, Any]) -> None:
        entry_low = fields.get("entry_low")
        entry_high = fields.get("entry_high")
        if entry_low is not None and entry_high is not None and entry_low > entry_high:
            raise ValueError("entry_low must be less than or equal to entry_high")

    @staticmethod

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Convert at the ingestion boundary: parse and validate numeric fields before building the payload, mapping text levels via an explicit dict.
  2. Treat placeholder strings ('N/A', '--', 'null') as None and omit the key.
  3. For percentages, strip '%' and divide: float(s.rstrip('%'))/100.
  4. Guard with the helper in validationCode before the API call.

Example fix

# before
service.create_signal({..., "confidence": "high"})  # float('high') → ValueError

# after
CONF = {"high": 0.9, "medium": 0.6, "low": 0.3}
conf = CONF.get(str(raw).lower())
if conf is None:
    try:
        conf = float(raw)
    except (TypeError, ValueError):
        conf = None
service.create_signal({..., "confidence": conf})
Defensive patterns

Strategy: type-guard

Validate before calling

def as_float(v):
    if v in (None, ''):
        return None
    try:
        return float(str(v).rstrip('%')) / (100 if isinstance(v, str) and v.strip().endswith('%') else 1)
    except (TypeError, ValueError):
        return None  # non-numeric → omit field
payload['confidence'] = as_float(payload.get('confidence'))

Type guard

def is_numeric_like(v) -> bool:
    if v in (None, ''):
        return True  # optional
    try:
        float(v)
        return True
    except (TypeError, ValueError):
        return False

Prevention

When it happens

Trigger: confidence: 'high' (free-text level instead of number), confidence: {'level': 0.8} (object), entry price fields receiving 'N/A' or '--' from a scraped table, booleans passing (float(True)=1.0) but strings like '80%' failing (the % sign).

Common situations: LLM outputs emitting confidence as a word ('高') that a mapping step skipped; scraped market data with placeholder strings for missing prices; schema change from string to numeric field with old producers unchanged; None-adjacent sentinels like 'null'/'nan-as-text' ('nan' actually parses — float('nan') succeeds — but 'null' fails).

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/2595c0563796a16f. Report an issue: GitHub.