{"record":{"id":"3da9b52826d89934","repo":"srbhr/Resume-Matcher","slug":"answer-text-must-not-be-blank","errorCode":null,"errorMessage":"answer text must not be blank","messagePattern":"answer text must not be blank","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"apps/backend/app/schemas/resume_wizard.py","lineNumber":49,"sourceCode":"\n\nclass ResumeWizardProgress(BaseModel):\n    \"\"\"Server-computed progress for the question card's bar.\"\"\"\n\n    current: int = 0\n    total: int = 8\n\n\nclass ResumeWizardAnswer(BaseModel):\n    \"\"\"User answer for one wizard turn.\"\"\"\n\n    text: str = Field(min_length=1, max_length=6000)\n\n    @field_validator(\"text\")\n    @classmethod\n    def _reject_blank(cls, value: str) -> str:\n        if not value.strip():\n            raise ValueError(\"answer text must not be blank\")\n        return value\n\n\nclass ResumeWizardHistoryEntry(BaseModel):\n    \"\"\"One answered question, with a pre-answer draft snapshot for Back.\"\"\"\n\n    question: str\n    answer: str\n    section: ResumeWizardSection\n    resume_data_before: ResumeData\n\n\nclass ResumeWizardState(BaseModel):\n    \"\"\"Complete state that round-trips between client and server.\"\"\"\n\n    step: ResumeWizardStep = \"intro\"\n    resume_data: ResumeData = Field(default_factory=ResumeData)\n    current_question: ResumeWizardQuestion = Field(default_factory=ResumeWizardQuestion)","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/schemas/resume_wizard.py#L31-L67","documentation":"ResumeWizardHistoryEntry.text is a required string of 1-6000 chars, and the _reject_blank field_validator additionally rejects values that are only whitespace. A whitespace-only answer (e.g. \" \") passes min_length but fails the strip() check, raising this ValueError during validation.","triggerScenarios":"Creating or validating a ResumeWizardHistoryEntry with text set to \"\", \" \", \"\\n\", \"\\t\", or any string whose strip() is empty.","commonSituations":"Frontend sends an empty answer after the user hits enter on a blank field; upstream text extraction trims content to whitespace; test fixtures with placeholder blanks.","solutions":["Ensure the caller sends non-whitespace answer text before constructing the model","Add an application-level check: if not answer.strip(): reject/reprompt the user","If the field may be legitimately absent, make it Optional instead of blank","Strip and re-check user input at the API boundary before deserialization"],"exampleFix":"// before\nResumeWizardHistoryEntry(text=\"   \", ...)\n// after\ntext = answer.strip()\nif not text:\n    raise HTTPException(422, \"answer text must not be blank\")\nResumeWizardHistoryEntry(text=text, ...)","handlingStrategy":"validation","validationCode":"def answer_is_valid(t: str) -> bool:\n    return isinstance(t, str) and 1 <= len(t) <= 6000 and bool(t.strip())","typeGuard":"def is_nonblank_str(v: object) -> TypeGuard[str]:\n    return isinstance(v, str) and v.strip() != \"\"","tryCatchPattern":"try:\n    entry = ResumeWizardHistoryEntry.model_validate(payload)\nexcept ValidationError as e:\n    raise HTTPException(422, \"answer text must not be blank\") from e","preventionTips":["Strip and check user answers client-side before submitting","Treat whitespace-only input as empty in all form handling","Bound-check length (1-6000) alongside blankness"],"tags":["pydantic","validation","blank-input"],"backgroundTag":"schema-validation-failed","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}