{"record":{"id":"955ceb2025f63cdd","repo":"ZhuLinsen/daily_stock_analysis","slug":"field-name-must-be-at-most-max-length-characte-955ceb","errorCode":null,"errorMessage":"{field_name} must be at most {max_length} characters","messagePattern":"(.+?) must be at most (.+?) characters","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"src/services/decision_signal_service.py","lineNumber":1199,"sourceCode":"        if not text:\n            raise ValueError(\"trigger_source is required\")\n        return text\n\n    @classmethod\n    def _normalize_optional_trigger_source(cls, value: Any) -> Optional[str]:\n        if value in (None, \"\"):\n            return None\n        return cls._normalize_trigger_source(value)\n\n    @staticmethod\n    def _optional_text(value: Any, field_name: str, *, max_length: int) -> Optional[str]:\n        if value is None:\n            return None\n        text = str(value).strip()\n        if not text:\n            return None\n        if len(text) > max_length:\n            raise ValueError(f\"{field_name} must be at most {max_length} characters\")\n        return text\n\n    @classmethod\n    def _optional_public_text(cls, value: Any, field_name: str, *, max_length: int) -> Optional[str]:\n        return cls._public_text(value, field_name, max_length=max_length, required=False)\n\n    @staticmethod\n    def _public_text(value: Any, field_name: str, *, max_length: int, required: bool) -> Optional[str]:\n        if value is None:\n            if required:\n                raise ValueError(f\"{field_name} is required\")\n            return None\n        text = sanitize_decision_signal_text(value)\n        if not text:\n            if required:\n                raise ValueError(f\"{field_name} is required\")\n            return None\n        if len(text) > max_length:","sourceCodeStart":1181,"sourceCodeEnd":1217,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/decision_signal_service.py#L1181-L1217","documentation":"ValueError from DecisionSignalService._optional_text (src/services/decision_signal_service.py:1199): optional free-text fields (notes, reasons, etc.) that are present and non-blank must not exceed max_length characters after stripping. Whitespace-only values are treated as absent (return None); anything longer than the cap raises '{field_name} must be at most {max_length} characters'. Each call site dictates the cap.","triggerScenarios":"Sending a notes/reason string longer than the field's configured cap — e.g. pasting an entire LLM analysis paragraph into a short free-text field, or a full error traceback into a remarks field. Multi-byte content counts by character, not bytes, so CJK text hits the cap slower but still hits it.","commonSituations":"UI textarea without maxlength bound to a capped backend field; concatenating multiple reasons with '\\n'.join() without trimming; logs forwarded into metadata text fields; changing the cap without notifying the frontend.","solutions":["Truncate client-side before the call: text.strip()[:max_length] (check the error message or field schema for the exact cap).","Move long-form content into a field designed for it (e.g. structured metadata/JSON signal_text) instead of a capped text column.","Bind frontend inputs to the same maxlength as the backend schema.","For machine-generated text, summarize or hash-and-store instead of inlining."],"exampleFix":"# before\nservice.create_signal({..., \"notes\": full_analysis_paragraph})  # 5000 chars > cap → ValueError\n\n# after\nnotes = full_analysis_paragraph.strip()[:500] or None\nservice.create_signal({..., \"notes\": notes})","handlingStrategy":"validation","validationCode":"MAX = 500  # use the cap from the field schema / error message\ntext = payload.get('notes')\nif text is not None:\n    text = str(text).strip() or None\n    payload['notes'] = text[:MAX] if text else None","typeGuard":"def fits(text: str | None, max_length: int) -> bool:\n    return text is None or 0 < len(text.strip()) <= max_length","tryCatchPattern":null,"preventionTips":["Truncate long text at the ingestion boundary instead of relying on server rejection.","Keep frontend maxlength synced with backend caps.","Put long-form content in structured metadata fields, not capped text columns."],"tags":["decision-signal","validation","length-limit","text-field"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}