{"record":{"id":"9101f0c6a37f0870","repo":"srbhr/Resume-Matcher","slug":"original-may-be-a-list-only-for-the-reorder-acti","errorCode":null,"errorMessage":"'original' may be a list only for the reorder action","messagePattern":"'original' may be a list only for the reorder action","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"apps/backend/app/schemas/models.py","lineNumber":919,"sourceCode":"    )\n    action: Literal[\"replace\", \"append\", \"reorder\", \"add_skill\"]\n    original: str | list[str] | None = Field(\n        default=None,\n        description=\"Current text at path — for verification. May be a list (the \"\n        \"current items) for the reorder action; only used for text verification of \"\n        \"replace/append, ignored otherwise.\",\n    )\n    value: str | list[str] = Field(description=\"New content\")\n    reason: str = Field(description=\"Why this change helps match the JD\")\n\n    @model_validator(mode=\"after\")\n    def _list_original_only_for_reorder(self) -> \"ResumeChange\":\n        \"\"\"A list ``original`` is only meaningful for ``reorder`` (the LLM sends\n        the current items). For the text actions it must stay a string/None — a\n        list there would silently bypass the replace verification gate and crash\n        the invented-metrics check, so reject it at parse time.\"\"\"\n        if isinstance(self.original, list) and self.action != \"reorder\":\n            raise ValueError(\"'original' may be a list only for the reorder action\")\n        return self\n\n\nclass ImproveDiffResult(BaseModel):\n    \"\"\"LLM output: a list of targeted resume changes.\"\"\"\n\n    changes: list[ResumeChange] = Field(default_factory=list)\n    strategy_notes: str = Field(default=\"\")\n","sourceCodeStart":901,"sourceCodeEnd":928,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/schemas/models.py#L901-L928","documentation":"ResumeChange is a Pydantic model representing one LLM-proposed resume edit. Its model validator _list_original_only_for_reorder rejects a list-valued 'original' field when action is anything other than 'reorder', because a list original for text actions would silently bypass the replace verification gate and break the invented-metrics check. The error is raised at parse/validation time with this exact message.","triggerScenarios":"Constructing or parsing a ResumeChange (e.g. from LLM JSON output via ImproveDiffResult) where original is a list (e.g. [\"item1\",\"item2\"]) but action is 'replace', 'add', or 'delete' instead of 'reorder'.","commonSituations":"LLM emits malformed change items; prompt/schema drift causes the model to send the current items list for a text edit; code that reuses the reorder payload shape for other actions.","solutions":["Check the action field of the failing ResumeChange; only 'reorder' accepts a list original","If the change is a text edit, convert original to a string (or null) before constructing the model","If items must be passed as a list, change action to 'reorder'","Tighten the LLM prompt/examples so list originals are only emitted for reorder"],"exampleFix":"// before\nResumeChange(action=\"replace\", original=[\"old text\"], replacement=\"new text\")\n// after\nResumeChange(action=\"replace\", original=\"old text\", replacement=\"new text\")","handlingStrategy":"validation","validationCode":"def is_valid_change(c: dict) -> bool:\n    return isinstance(c.get(\"original\"), list) == (c.get(\"action\") == \"reorder\")","typeGuard":"def has_list_original(c: ResumeChange) -> bool:\n    return isinstance(c.original, list)","tryCatchPattern":"try:\n    change = ResumeChange.model_validate(raw)\nexcept ValidationError as e:\n    logger.warning(\"dropping invalid change: %s\", e)\n    change = None","preventionTips":["Only emit list originals with action='reorder' in LLM prompts","Validate raw LLM change dicts with is_valid_change before model construction","Add unit tests covering each action with both string and list originals"],"tags":["pydantic","validation","llm"],"backgroundTag":"schema-validation-failed","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}