ZhuLinsen/daily_stock_analysis · error · ValueError
score must be between 0 and 100
Error message
score must be between 0 and 100
What it means
ValueError from DecisionSignalService payload normalization (src/services/decision_signal_service.py:816): the optional `score` field, when present in the create/update payload, must be an integer in [0, 100]. _optional_int first coerces it, then the explicit range check rejects anything below 0 or above 100 (non-integers fail earlier with 'score must be an integer').
Source
Thrown at src/services/decision_signal_service.py:816
metadata = dict(raw_metadata)
else:
raise ValueError("metadata must be an object")
if "decision_profile" in payload:
decision_profile = normalize_decision_profile(payload.get("decision_profile"))
if decision_profile is None:
allowed = ", ".join(VALID_DECISION_PROFILES)
raise ValueError(f"decision_profile must be one of: {allowed}")
else:
decision_profile = extract_legacy_decision_profile(metadata) or "balanced"
metadata = self._synchronize_metadata_decision_profile(metadata, decision_profile)
confidence = self._optional_float(payload.get("confidence"), "confidence")
if confidence is not None and not 0.0 <= confidence <= 1.0:
raise ValueError("confidence must be between 0.0 and 1.0")
score = self._optional_int(payload.get("score"), "score")
if score is not None and not 0 <= score <= 100:
raise ValueError("score must be between 0 and 100")
market_phase = self._normalize_optional_enum(payload.get("market_phase"), MARKET_PHASES, "market_phase")
horizon_explicit = self._payload_has_value(payload, "horizon")
horizon = self._normalize_optional_enum(payload.get("horizon"), HORIZONS, "horizon")
horizon_defaulted = False
if horizon is None:
horizon = self._default_horizon(action=action, market_phase=market_phase)
horizon_defaulted = horizon is not None and not horizon_explicit
expires_explicit = self._payload_has_value(payload, "expires_at")
expires_at = self._parse_datetime(payload.get("expires_at"))
if expires_at is None and not expires_explicit:
expires_at = self._default_expires_at(
horizon=horizon,
market=market,
metadata=metadata,
)
created_at = self._parse_datetime(payload.get("_created_at_override"))
View on GitHub (pinned to 5159bd72e8)
Solutions
- Rescale the input to 0–100 before sending (e.g. round(score_0_to_10 * 10), int(prob * 100)).
- Validate bounds client-side before the API call (see validationCode).
- If a non-integer arrives, round/convert explicitly rather than letting _optional_int reject it.
- Audit upstream producers after any model/prompt version change for scale drift.
Example fix
# before
service.create_signal({"stock_code": "600519", "market": "cn", "action": "buy", "score": 7.5 * 100}) # 750 → ValueError
# after
raw = 7.5 # 0-10 scale
service.create_signal({"stock_code": "600519", "market": "cn", "action": "buy", "score": round(raw * 10)}) # 75 Defensive patterns
Strategy: validation
Validate before calling
def valid_score(v) -> bool:
return v is None or (isinstance(v, int) and not isinstance(v, bool) and 0 <= v <= 100)",
then: assert valid_score(payload.get('score')) — 'score must be int in [0, 100]' Type guard
def normalize_score(v) -> int | None:
if v in (None, ''):
return None
n = int(round(float(v)))
if not 0 <= n <= 100:
raise ValueError('score out of 0-100; rescale upstream')
return n Prevention
- Pin the 0–100 integer contract in client schemas and JSON-schema validation of LLM outputs.
- Rescale at the ingestion boundary (0–10 → ×10, probability → ×100).
- Add contract tests for scale after any model/prompt version bump.
When it happens
Trigger: POST/PATCH decision-signal payloads with score: -5, score: 101, score: 8500 — typically from raw sentiment outputs on other scales (0–10, -1..1, 0–1000). Note sentiment_score in the 0–1 style is a different field; `score` here is the integer 0–100 confidence/conviction score.
Common situations: Feeding a 0–10 LLM sentiment score directly as score (×10 missing); passing a probability (0–1) that rounds to 0/1 and looks suspicious but passes — or 1.5 which fails as non-integer; upstream model version changing its score scale; unit tests asserting old bounds.
Related errors
- {field_name} must be a finite positive number
- entry_low must be less than or equal to entry_high
- {field_name} must be between {minimum} and {maximum}
- {field_name} must be between {minimum:g} and {maximum:g}
- {field_name} must be an integer
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/f30c6cbdc819a9be.
Report an issue: GitHub.