ZhuLinsen/daily_stock_analysis · error · ValueError
{field_name} must be an integer
Error message
{field_name} must be an integer What it means
_optional_positive_int converts an optional numeric field with int(value); if value is neither None/'' nor int-convertible (e.g. a non-numeric string, a dict, a list), TypeError/ValueError is caught and re-raised as ValueError '<field> must be an integer'. It validates API query/path params like signal_id before they reach the repository.
Source
Thrown at src/services/decision_signal_outcome_service.py:631
if horizon:
return [horizon]
return list(SUPPORTED_OUTCOME_HORIZONS.keys())
def _require_existing_signal(self, signal_id: int) -> DecisionSignalRecord:
signal_id_norm = self._optional_positive_int(signal_id, "signal_id")
row = self.signal_repo.get(signal_id_norm)
if row is None:
raise DecisionSignalNotFoundError(f"Decision signal not found: {signal_id_norm}")
return row
@staticmethod
def _optional_positive_int(value: Any, field_name: str) -> Optional[int]:
if value in (None, ""):
return None
try:
number = int(value)
except (TypeError, ValueError) as exc:
raise ValueError(f"{field_name} must be an integer") from exc
if number <= 0:
raise ValueError(f"{field_name} must be positive")
return number
@staticmethod
def _normalize_enum(value: Any, allowed: Iterable[str], field_name: str) -> str:
text = str(value or "").strip()
allowed_set = set(allowed)
if text not in allowed_set:
allowed_text = ", ".join(sorted(allowed_set))
raise ValueError(f"{field_name} must be one of {allowed_text}")
return text
@classmethod
def _normalize_optional_enum(cls, value: Any, allowed: Iterable[str], field_name: str) -> Optional[str]:
if value in (None, ""):
return None
return cls._normalize_enum(value, allowed, field_name)View on GitHub (pinned to 5159bd72e8)
Solutions
- Send the field as a plain integer (or integer string like "42").
- Coerce and validate on the client before the call: use int() and confirm it is whole.
- For float strings, convert via int(float(value)) first if fractional input is legitimate.
Example fix
# before
result = service._optional_positive_int("12.5", "signal_id")
# after
result = service._optional_positive_int(int(float("12.5")), "signal_id") # 12 Defensive patterns
Strategy: type-guard
Validate before calling
def toOptionalInt(value) -> int | None:
if value in (None, ""):
return None
try:
return int(str(value).strip())
except (TypeError, ValueError):
raise HTTPException(400, f"{value!r} is not an integer")
signal_id = toOptionalInt(raw_id) # before calling the service Type guard
def isIntLike(value: object) -> bool:
if value in (None, ""):
return True
try:
int(value)
return True
except (TypeError, ValueError):
return False Try / catch
try:
result = service.evaluate_outcomes(signal_id=raw_id)
except ValueError as exc:
if "must be an integer" in str(exc):
return JSONResponse(status_code=400, content={"error": "invalid_params", "message": str(exc)})
raise Prevention
- Declare id fields as int in Pydantic request models so FastAPI validates first.
- Avoid float strings ('1.5') for ids; convert via int(float(x)) only when fractions are meaningful.
- Keep URL-path ids as plain integers.
When it happens
Trigger: Calling decision-signal endpoints/services with signal_id="abc", signal_id=[1], or a float string like "1.5" — int("1.5") raises ValueError; passing an object with no __int__ raises TypeError.
Common situations: String IDs from URL paths or query strings not validated upstream; JSON payloads where the field is typed as string; frontend sending "1.0".
Related errors
- {field_name} must be positive
- eval_window_days must be a positive integer
- {field_name} must be one of {allowed_text}
- {field_name} must be at most {max_length} characters
- unsupported_report_type
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/2a5e1e346d0fd045.
Report an issue: GitHub.